Skip to Content
SDKAPI reference

API reference

Reference for the browser SDK (@ceros/markup-sdk): the MarkUpSDK class, its services, the event system, and error handling. For init/render options see Configuration; for the lower-level transport see Headless client.


MarkUpSDK

MarkUpSDK is a singleton. The lifecycle is init → render → destroy.

MemberSignatureDescription
MarkUpSDK.initstatic init(options: MarkUpSDKOptions): MarkUpSDKValidate options and construct the singleton. Calling it twice without destroy returns the existing instance and logs a warning. Throws ConfigurationError on invalid options.
MarkUpSDK.getInstancestatic getInstance(): MarkUpSDK | nullThe current instance, or null if not initialized.
renderrender(options?: RenderOptions): thisMount the UI and run the auth gate. /ui entry only. Throws SDKStateError if not initialized.
destroydestroy(): voidUnmount UI, cancel in-flight work, clear auth and stores, and release the singleton slot. Safe to call repeatedly.
isInitializedisInitialized(): booleanWhether init has run.
isRenderedisRendered(): booleanWhether the UI is mounted.
getConfiggetConfig(): Readonly<…>The immutable resolved configuration.
setKeyboardShortcutsEnabledsetKeyboardShortcutsEnabled(enabled: boolean): voidToggle global shortcuts at runtime. /ui entry only.
on / off / oncesee EventsSubscribe to and unsubscribe from SDK events.

The four service accessors below are available on every instance:

markup.projects // ProjectsService markup.threads // ThreadsService markup.comments // CommentsService markup.uploads // UploadsService

Services

markup.projects

MethodDescription
get(markupId?)Fetch a project. Defaults to the configured markupId.
getTagData(markupId?)Fetch taggable/tagged users for a project.
listViewModes(markupId?)List view modes (device breakpoints) for a project.
setReadOnly(readOnly, markupId?)Pause or resume new comments on the project.

markup.threads

MethodDescription
list(params?)List threads for the project (paginated).
get(threadId)Fetch a single thread, including its messages.
resolve(threadId)Resolve a thread; returns the updated snapshot.
unresolve(threadId)Re-open a resolved thread.
setPriority(threadId, priority)Set the thread’s priority (or null to clear it).
delete(threadId)Delete a thread and remove its pin.
deleteReply(threadId, replyId)Delete a reply from a thread.
createFromPin(data, attachments?)Create a thread from pin-placement data.
movePin(threadId, placement)Move a thread’s pin to a new location.
createReplySnapshot(threadId, message, attachments?, mentionIds?)Add a reply and return the refreshed thread.
editThreadMessage(threadId, message, attachments?, mentionIds?)Edit a thread’s root message.
editReply(threadId, replyId, message, attachments?, mentionIds?)Edit a reply.

markup.comments

MethodDescription
list({threadId})List comments for a thread.
listByThread(threadId)List all comments for a thread by ID.
create(input)Create a new comment (reply) in a thread.

markup.uploads

MethodDescription
upload(file, resourceType, options?)Upload via the presigned-S3 policy flow. Supports progress (options.onProgress) and cancellation (options.signal).
uploadDirect(file, resourceType, options?)Direct multipart upload. Simpler, but limited to files under 20 MB and without progress.

Data-model types returned by these services (Project, Thread, Comment, ThreadStatus, ThreadPriority, PaginatedResponse<T>, and more) are re-exported from @ceros/markup-sdk — import them directly for typed handling. They’re defined in the headless client.


Events

Subscribe with markup.on(event, handler); the returned function unsubscribes. Use markup.off(event, handler) to remove a specific listener, or markup.once(event, handler) to auto-unsubscribe after the first emission.

EventPayload
commenting:startundefined
commenting:stopundefined
pin:placedPinPlacedData
pin:cancelledundefined
thread:open{threadId: string}
thread:closeundefined
thread:priority-changed{threadId: string; priority: ThreadPriority | null}
comment:reply{threadId: string; message: string}
comment:resolve{threadId: string}
comment:unresolve{threadId: string}
comment:delete{threadId: string}
comment:reply:delete{threadId: string; replyId: string}
comment:edit{threadId: string}
comment:reply:edit{threadId: string; replyId: string}
comment:scroll-to-pin{threadId: string}
Subscribe to events
const off = markup.on("thread:open", ({threadId}) => { analytics.track("markup_thread_open", {threadId}); }); // Later off();

Errors

All SDK errors extend MarkUpSDKError.

ClassThrown when
MarkUpSDKErrorBase class. Carries code (an SDKErrorCode), message, and an optional cause.
ConfigurationErrorinit is called with invalid options (missing publicKey/markupId, malformed markupId).
SDKStateErrorThe SDK is used incorrectly, e.g. render() before init().

SDKErrorCode is re-exported from @ceros/markup-sdk-core and includes the sign-in codes (POPUP_BLOCKED, POPUP_CLOSED, WORKSPACE_FORBIDDEN, SDK_SIGNIN_FAILED) described in Authentication.

Handle SDK errors
import {MarkUpSDKError, SDKErrorCode} from "@ceros/markup-sdk/ui"; try { await markup.threads.resolve(threadId); } catch (err) { if (err instanceof MarkUpSDKError && err.code === SDKErrorCode.WORKSPACE_FORBIDDEN) { // user isn't a member of this workspace } else { throw err; } }

Notifications

The /ui entry exports a notify snackbar helper (it is not available on the headless entry):

notify
import {notify} from "@ceros/markup-sdk/ui"; notify.success("Comment posted"); notify.error("Couldn't load comments.", {description: err.message});

notify.success / info / warning / error(message, opts?) render an in-overlay snackbar with auto-dismiss (4s / 4s / 6s / 8s by default). NotifyOptions accepts description, an action ({label, onClick}), timeoutMs, and dismissible.


End-to-end example

Full integration
import {MarkUpSDK} from "@ceros/markup-sdk/ui"; const markup = MarkUpSDK.init({ publicKey: "<YOUR_PUBLIC_KEY>", markupId: "<YOUR_MARKUP_ID>", onTokenNeeded: async () => { const res = await fetch("/api/markup-token"); const {token} = await res.json(); return token; } }); markup.render({position: "bottom-right", theme: "auto"}); const offOpen = markup.on("thread:open", async ({threadId}) => { const thread = await markup.threads.get(threadId); console.log(`${thread.replies.length} replies`, thread.status); }); // On unmount offOpen(); markup.destroy();

Next steps