Guest API

Everything a view calls funnels through one client — @conexus-x/sdk’s default export. No fetch, no token and no CRM base URL anywhere in this package on purpose: a view that cannot address the API directly is a view that cannot be tricked into addressing it with someone else’s credentials.

js
import conexus from "@conexus-x/sdk";

const cx = conexus();
const { context, settings, grantedScopes } = await cx.connect();

Connection

CallWhat it does
cx.connect()Handshake. Resolves { context, settings, grantedScopes, hostVersion }. Safe to call from ten components — one handshake happens.
cx.contextThe last context the host sent, kept current by pushes. null before connect.
cx.hasScope(scope)Feature-detect. Never assume a scope was granted just because it was requested.
cx.getContext()Fetch the context fresh, rather than reading the cached value.
cx.getSettings()Fetch the view's settings fresh.
cx.destroy()Drop every listener and reject anything still in flight.

Subscribing

cx.listen(topic, handler) subscribes to a host push and returns the unsubscribe function — handing it straight back as a React effect’s cleanup is the whole reason it is shaped this way.

TopicPayload
contextThe live ViewContext — pushed again whenever the person moves: switches collection, the module changes.
settingsThe settings object configured for this view instance.
selection{ recordIds: string[] } — rows selected on the module.
changeThe CRM realtime envelope, already filtered to this module.
theme{ theme: "light" | "dark" } when the host's theme flips.

Reading and writing data

cx.api.* are typed helpers over the same allowlist described in Scopes & routes — thin on purpose, they build a path and hand back the unwrapped payload.

js
const records = await cx.api.records.list(context.collectionId);

if (cx.hasScope("records:write")) {
    await cx.api.records.update(records[0]._id, { isCompleted: true });
}

cx.api.modules

list(workspaceId) · create(workspaceId, body) · update(moduleId, body) · remove(moduleId)

cx.api.collections

list(moduleId) · create(moduleId, body) · update(collectionId, body) · remove(collectionId)

cx.api.columns

list(moduleId) · create(moduleId, body) · update(columnId, body) · remove(columnId)

cx.api.records

list(collectionId) · create(collectionId, body) · update(recordId, body) · remove(recordId) · subRecords(recordId) · createSubRecord(recordId, body)

cx.api.values

forRecord(recordId) · set({ record, column, value }) · update(recordValueId, { value }) · clear(recordValueId)

cx.api.amendments

list(recordId) · post(recordId, { message, parentComment? })

cx.api.members / cx.api.activity

members.list(workspaceId) and activity.list(workspaceId, query?) — both read-only; there is no members/activity write surface for a view at all.

The raw call

cx.request({ method, path, query, body }) reaches an endpoint the typed helpers do not cover yet — still checked against the exact same allowlist and scopes.

Commands — asking the host to do something

Things only the host can do, because they touch chrome the iframe cannot reach. An unimplemented command rejects with command_unsupported rather than resolving quietly, so a view can feature-detect instead of assuming it worked.

CommandParamsResolves to
notice{ message, type?, timeoutMs? }void — a toast in the host chrome
openRecord{ recordId }void — opens the record panel the module already has
confirm{ message, confirmLabel?, cancelLabel? }boolean — the answer
resize{ height? }void — omit height to fit content
navigate{ path }void — paths only, never a foreign origin
copyToClipboard{ text }void — through the host, which has the user gesture

Storage

cx.storage.get/set/delete/keys — a small key-value store scoped to THIS mount of the view. For view state (a chosen grouping, a collapsed panel), not customer data.

Sizing itself

cx.autoResize(element?) keeps the iframe as tall as its content, via a ResizeObserver. Opt-in — a view that draws its own scroll area does not want it.

Errors

Every failure rejects with a ConexusError carrying a code. Branch on the code — the message is for your console and may be reworded.

CodeMeaning
protocol_mismatchThe host and SDK speak different major protocol versions. Upgrade @conexus-x/sdk.
not_connectedNot embedded in an iframe, or connect() hasn't resolved yet.
timeoutThe host did not answer within the request/handshake timeout.
scope_deniedThe call needs a scope this view was not granted.
route_deniedThe endpoint is not on the allowlist at all.
bad_requestThe CRM API rejected the request — see .status and .details.
command_unsupportedThis host build does not implement that command.
storage_unavailableThe host's storage backend is down.
host_errorThe host returned an error with no further detail.
api_errorThe proxied CRM API call failed — .status carries its HTTP status.