Docs / Contact / sdks/javascript/api-reference

JavaScript SDK: API Reference

TendrlClient#

new TendrlClient(options)#

Create a new client instance. See Configuration for all options.

javascript

const client = new TendrlClient({ apiKey: 'your_key' });

client.start()#

Start the client: marks the entity online, starts the batch sender, and — only if a callback is already set — starts message polling. Calling it twice is a no-op (with a console warning under debug: true).

Unlike the Go SDK, this performs no API-key validation. A bad key surfaces as failed sends, which are silent unless debug: true or offlineStorage: true.

client.stop()#

Stop the client: marks the entity offline and clears both intervals. Anything still in the queue is dropped rather than flushed, so publish what you need before calling it.

Publishing#

client.publish(data, tags?, entity?, waitResponse?)#

Publish a message to Contact.

Parameter Type Default Description
data any JSON-serializable data
tags string[] [] Tags for routing to flows (max 10)
entity string "" Target entity. Empty sends to self.
waitResponse boolean false true = send immediately and return a promise for the response

Returns: a Promise for the server's response when waitResponse is true, otherwise undefined — the message has been queued, not delivered.

javascript

client.publish({ temp: 23.5 }, ['sensor']);
client.publish({ cmd: 'reboot' }, ['admin'], 'device-001');
await client.publish({ alert: 'high' }, ['alert'], '', true);

Message Receiving#

There is one receive mechanism: a single callback, set on the constructor (callback) or with setMessageCallback. The SDK has no tag router — client.on() and client.onDefault() do not exist and throw TypeError: client.on is not a function. Branch on message.tags yourself.

javascript

client.setMessageCallback((message) => {
    if ((message.context?.tags || []).includes('ai-response')) {
        console.log(message.data);
    }
});
Tags arrive under `context`, not at the top level

The SDK rewrites each inbound message before handing it to your callback, moving the server's top-level tags array into message.context.tags — and omitting context entirely when there are no tags. Read message.context?.tags, not message.tags, which is always undefined. This differs from the Python SDK, which passes the server's payload through untouched.

client.setMessageCallback(fn)#

Set the message callback. Throws TypeError if fn is not a function. Setting one on a running client starts polling immediately. The callback receives:

js

{
    msg_type: 'publish',          // 'command' when the server omits it
    source: 'account:region:entity:name',
    timestamp: '2025-01-15T10:30:00Z',
    data: { ... },
    context: { tags: ['tag1'] },  // absent entirely when the message has no tags
    dest: 'device-001',           // only when the server sends one
    request_id: 'req-123'         // only when the server sends one
}

Whatever the callback returns is ignored; returning false only logs a warning under debug: true. A callback that throws is caught and the next message is still delivered.

client.setMessageCheckRate(ms)#

Set polling interval in milliseconds.

client.setMessageCheckLimit(n)#

Set maximum messages retrieved per poll.

client.checkMessages(limit?)#

Poll once for incoming messages and pass each to the callback. Returns a Promise that resolves when the round trip finishes; the messages themselves arrive only through the callback.

client.processOfflineMessages() → Promise#

Replay messages held in IndexedDB, in batches of 50, deleting only the ones that arrive. Called automatically when the connectivity probe recovers. No-op when offlineStorage is off.

File Transfer#

The JavaScript SDK has no file-transfer methods. sendFile, checkFiles, downloadFile and rescanFile are Go and MicroPython features. To move files from JavaScript, call the /entities/files REST endpoints directly — see File Transfer.

State Table#

The JavaScript SDK does not wrap the state table in either direction: there is no getState, updateState, replaceState, onState or checkState, and stateCallback is not a valid constructor option. Call the REST endpoints with fetch:

Method Path Purpose
GET /api/entities/status-table Read current state
PATCH /api/entities/status-table Merge into existing state
PUT /api/entities/status-table Replace state entirely

See State Table and Examples.

Heartbeat#

client.sendHeartbeat(data) → Promise#

Send one heartbeat with system metrics, immediately, and resolve with the server's response. Every field is optional and each must be non-negative or the call throws. There is no automatic heartbeat loop in this SDK — you choose the cadence.

Field Type Description
mem_free number Free memory in bytes
mem_total number Total memory in bytes
disk_free number Free disk space in bytes
disk_size number Total disk space in bytes

Connectivity#

client.checkConnectionState() → Promise<boolean>#

Check if the Contact API is reachable. Results are cached for 30 seconds.

React Hook#

useTendrlClient(options)#

React hook that manages client lifecycle automatically.

javascript


const {
    client,              // TendrlClient instance (null until the mount effect runs)
    isConnected,         // boolean - snapshot, see the caution below
    publish,             // (data, tags, entity, wait) => Promise | undefined
    checkMessages,       // (limit) => Promise
    setMessageCallback,  // (fn) => void
    setMessageCheckRate, // (ms) => void
    setMessageCheckLimit,// (n) => void
    sendHeartbeat        // (data) => Promise
} = useTendrlClient({
    onMessage: (msg) => console.log(msg.data),
    apiBaseUrl: 'http://localhost:8000',  // optional; falls back to TENDRL_APP_URL
    // ...all other TendrlClient options
});

The hook:

The hook takes no `apiKey` option

useTendrlClient({ apiKey }) is silently ignored. The key comes from REACT_APP_TENDRL_KEY and nowhere else; if that variable is unset the hook logs "TENDRL_KEY environment variable is missing." to the console and creates no client, leaving client as null and publish a no-op.

`isConnected` is not reactive

It is read off the client instance during render rather than held in React state, so a connectivity change does not re-render the component. It is also false on the first render, because the client is created in an effect that runs afterwards. For live status, poll client.checkConnectionState() in an effect and store the result with useState.