Skip to main content
@custral/js is the browser-facing, publishable-key surface, separate from the secret-key @custral/sdk. It is the protocol layer the other browser packages build on:
  • CustralUsage: report per-customer usage events against a Custral usage module, and read them back.
  • CustralError and friends: one error hierarchy to catch, whatever failed.
  • request(): the raw {data, error, reqId} envelope, unwrapped, if you are calling an endpoint the package does not wrap yet.
Looking for the chat widget? It is @custral/widget, which builds on this package. CustralChat used to ship here under @custral/ui@1.x; it moved, because the only thing that ever used it was the widget itself.
@custral/ui@1.x is deprecated. It contained no UI: it held the chat engine and the usage client, which now live in @custral/widget and @custral/js respectively. Nothing about the API changed, so migrating is a rename of the import. @custral/ui 2.0 is a different package: React hooks over your workspace data, documented as the React data SDK.

Install

socket.io-client is an optional peer, needed only if you import the @custral/js/socket subpath. Usage tracking and the error types pull in nothing.

Usage tracking

CustralUsage records that a customer consumed a metered product. It is safe to call from a browser at the moment of consumption, which is the point: your own application is the only thing that knows a unit was used.
key is a publishable (pk_…) or secret (sk_…) key; moduleId is the usage module (umod_…) events are recorded against. baseUrl defaults to https://api.custral.com and timeoutMs to 30 seconds.
Records one event. It needs a customer, plus a product or an event name such as report_created. user is the person inside the account: your own user id or their email. quantity defaults to 1 and occurredAt to now. An occurredAt that cannot be read is refused by name rather than throwing a bare RangeError. If an identifier matches no record, the result’s unmatched names that side (["customer"]). The event is kept, but counts toward no record’s usage.
Reads recorded events back, newest first. Requires a secret key holding usage:read; a publishable key is refused with insufficient_scope.
Reading events is paged at 50 by default (200 max). A usage event is written per metered call in your own application, so this is the one list whose length is not bounded by anything a person does in the workspace. Filter by date to total a period rather than paging to the end.
The key option was called publishableKey under @custral/ui. That spelling still works and is deprecated: the same client now takes a secret key too, so the old name described only half of what it accepts.

Errors

Every call throws a CustralError subclass. Custral’s API answers HTTP 200 with the failure inside the envelope, so status alone is not the signal, and this is the one place that is decided.
Every one carries code, statusCode and requestId. Quote the req_… when you contact support: it is what makes a failure findable in our logs.

Realtime

@custral/js/socket is a subpath, not part of the base entry, because socket.io-client is browser-only and around 40KB. Import it only where you actually open a socket.
The connection forces the WebSocket transport. Custral runs multiple API instances behind a load balancer with no sticky sessions, so HTTP long-polling pins a session to one instance and fails whenever a poll lands on another. A client that cannot open a WebSocket gets no realtime at all, so anything built on this needs an HTTP path to the same data.

Troubleshooting

Reading events needs a secret key (sk_…) holding usage:read. Writing one with track() is publishable-key authed so it can be called from a browser; reading them back is not, or a client-side key would let anyone enumerate your workspace’s usage.
The constructor needs key (or the deprecated publishableKey). Create one in Settings → Applications.
@custral/js in the browser with a publishable key for usage tracking. @custral/widget for the React chat widget. @custral/ui for React hooks over records and objects. @custral/sdk on a server with a secret key for records, objects, and webhooks.
CustralUsage and the error types are now in @custral/js; CustralChat is in @custral/widget. Change the import and nothing else.