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.
| Member | Signature | Description |
|---|---|---|
MarkUpSDK.init | static init(options: MarkUpSDKOptions): MarkUpSDK | Validate options and construct the singleton. Calling it twice without destroy returns the existing instance and logs a warning. Throws ConfigurationError on invalid options. |
MarkUpSDK.getInstance | static getInstance(): MarkUpSDK | null | The current instance, or null if not initialized. |
render | render(options?: RenderOptions): this | Mount the UI and run the auth gate. /ui entry only. Throws SDKStateError if not initialized. |
destroy | destroy(): void | Unmount UI, cancel in-flight work, clear auth and stores, and release the singleton slot. Safe to call repeatedly. |
isInitialized | isInitialized(): boolean | Whether init has run. |
isRendered | isRendered(): boolean | Whether the UI is mounted. |
getConfig | getConfig(): Readonly<…> | The immutable resolved configuration. |
setKeyboardShortcutsEnabled | setKeyboardShortcutsEnabled(enabled: boolean): void | Toggle global shortcuts at runtime. /ui entry only. |
on / off / once | see Events | Subscribe 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 // UploadsServiceServices
markup.projects
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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.
| Event | Payload |
|---|---|
commenting:start | undefined |
commenting:stop | undefined |
pin:placed | PinPlacedData |
pin:cancelled | undefined |
thread:open | {threadId: string} |
thread:close | undefined |
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} |
const off = markup.on("thread:open", ({threadId}) => {
analytics.track("markup_thread_open", {threadId});
});
// Later
off();Errors
All SDK errors extend MarkUpSDKError.
| Class | Thrown when |
|---|---|
MarkUpSDKError | Base class. Carries code (an SDKErrorCode), message, and an optional cause. |
ConfigurationError | init is called with invalid options (missing publicKey/markupId, malformed markupId). |
SDKStateError | The 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.
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):
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
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
- Headless client — the typed
ApiClientand full data-model types. - Configuration —
initandrenderoptions.