@flxsense/flexsignal-sdk
v1.1.0
Published
SDK for the FlexSignal API
Downloads
1,114
Readme
@flxsense/flexsignal-sdk
TypeScript SDK for the FlexSignal API.
This package wraps the FlexSignal HTTP API with typed resources for authentication, account management, notifications, geocoding, devices, assets, units, spaces, and storage data.
Installation
pnpm add @flxsense/flexsignal-sdkRequirements
- Node.js with
fetch,FormData, andURLsupport - FlexSignal client credentials for client-authenticated endpoints
Quick Start
import { FlexsignalClient } from '@flxsense/flexsignal-sdk';
const client = new FlexsignalClient({
client_id: process.env.FLEXSIGNAL_CLIENT_ID,
client_secret: process.env.FLEXSIGNAL_CLIENT_SECRET,
});
const auth = await client.auth.login_password({
email: '[email protected]',
password: 'secret',
});
if (auth.kind !== 'ready') {
throw new Error(`Login failed: ${auth.kind}`);
}
const session = auth.session;
const units = await session.units.list();
if (!units.ok) {
throw new Error(units.error.message);
}
console.log(units.data);Client Setup
import { FlexsignalClient } from '@flxsense/flexsignal-sdk';
const client = new FlexsignalClient({
client_id: process.env.FLEXSIGNAL_CLIENT_ID,
client_secret: process.env.FLEXSIGNAL_CLIENT_SECRET,
url: 'https://api.flexdevel.com/',
version: 'v1',
ident: 'my-app',
dev: false,
});Available options:
client_id: client id used for basic-auth protected endpointsclient_secret: client secret used for basic-auth protected endpointsurl: API base URL, defaulthttps://api.flexdevel.com/version: API version path, defaultv1ident: client identifier used by notification endpoints, defaultflexsignal-jstoken_expires_in: optional access token lifetime overrideauto_refresh_tokens: automatically refreshes tracked session bearer tokens on401, defaulttruedev: enables debug logging and defaults token lifetime to300seconds
The client also exposes lower-level hooks from @jeppech/typed-fetcher:
client.use(handler)for middlewareclient.on_error(handler)for fetcher error observersclient.retry(handler, options)for retry behavior
Authentication
Password Login
const result = await client.auth.login_password({
email: '[email protected]',
password: 'secret',
});
switch (result.kind) {
case 'ready':
console.log(result.tokens.access_token);
break;
case 'mfa_required':
console.log('Request TOTP passcode from the user');
break;
case 'invalid_credentials':
console.error(result.message);
break;
case 'error':
console.error(result.error);
break;
}Password + TOTP Login
const result = await client.auth.login_password_otp({
email: '[email protected]',
password: 'secret',
passcode: '123456',
});Restore a Session From Existing Tokens
const restored = client.auth.restore_session({
access_token,
refresh_token,
access_expires_at,
refresh_expires_at,
});
if (!restored.ok) {
throw new Error(restored.error.message);
}
const session = restored.data;restore_session(...) accepts the same TokenBundle shape returned from login and refresh calls, so you can persist result.tokens directly and restore from that object later.
const savedTokens = result.tokens;
const restored = client.auth.restore_session(savedTokens);Refresh a Session
const refreshed = await session.refresh();
if (!refreshed.ok) {
throw new Error(refreshed.error.message);
}
console.log(session.tokens);
console.log(session.tokens.access_expires_at);
console.log(session.tokens.refresh_expires_at);When auto_refresh_tokens is enabled, session-backed requests also retry automatically once after a 401 by refreshing the tracked session's bearer token.
Automatic refresh only applies to tracked AuthenticatedSession instances created through login, session restore, or client.create_session(...).
Dispose a Session
For long-lived server processes, call session.dispose() when the session is no longer needed to stop SDK tracking and automatic refresh attempts.
const auth = await client.auth.login_password({
email: '[email protected]',
password: 'secret',
});
if (auth.kind !== 'ready') {
throw new Error(`Login failed: ${auth.kind}`);
}
const session = auth.session;
try {
const units = await session.units.list();
if (!units.ok) {
throw new Error(units.error.message);
}
console.log(units.data);
} finally {
session.dispose();
}Notes:
- calling
dispose()more than once is safe - disposed sessions are no longer auto-refreshed or tracked by the client
session.refresh()returns aninvalid_tokenerror after disposaldispose()does not revoke tokens on the server
Session Resources
Authenticated sessions expose these resource groups:
session.accountsession.assetssession.devicessession.spacessession.storagesession.units
Account
const totp = await session.account.setup_totp({ qr_size_px: 256 });
const recoveryCodes = await session.account.list_recovery_codes({ count: 10 });
const tokenInfo = await session.account.introspect_access_token();Available methods:
list_recovery_codes({ count? })setup_totp({ qr_size_px? })enable_totp({ passcode })introspect_access_token()
Units
const created = await session.units.create({
name: 'Trailer sensors',
device_ids: ['e9c41094-f691-4d39-9e2b-06cc9bc6b137', 'b7613970-75fe-44b3-a86d-fee456b94b98'],
});
const units = await session.units.list();Available methods:
list()get({ unit_id })create({ name, device_ids })update({ unit_id, name, device_ids })delete({ unit_id })
Assets
const assets = await session.assets.list();
const latestLocation = await session.assets.get_last_location({ asset_id: '1468b6f8-e750-43b4-9ffd-8caa01107ed5' });Available methods:
list()get({ asset_id })create({ name, unit_ids })update({ asset_id, name, unit_ids })delete({ asset_id })list_locations({ asset_id })get_last_location({ asset_id })
Devices
const devices = await session.devices.list();Available methods:
list()
Spaces
await session.spaces.create_or_get({
namespace: 'app',
key: 'theme',
value: 'dark',
});Available methods:
create({ namespace, key, value })get({ namespace, key })create_or_get({ namespace, key, value })update({ namespace, key, value })delete({ namespace, key })
Storage
Supported storage keys:
locationvarioustempwindrainhumidevent
Supported groups for grouped queries:
deviceunitasset
const latest = await session.storage.list_latest({ type: 'location' });
const history = await session.storage.list_range({
group: 'unit',
type: ['temp', 'humid'],
id: 'e9c41094-f691-4d39-9e2b-06cc9bc6b137',
begin: '2026-01-01T00:00:00Z',
end: '2026-01-02T00:00:00Z',
});Available methods:
list_latest({ type })list_last({ group, type, id })list_range({ group, type, id, begin, end })
Client Resources
The root client exposes resources that do not require an authenticated session.
Accounts
const account = await client.accounts.register({
email: '[email protected]',
password: 'secret',
username: 'user',
});Available methods:
create({ email, password, username? })register({ email, password, username? })change_password({ email, password })
Notifications
await client.notifications.send_email_template({
sender: '[email protected]',
to: '[email protected]',
template_name: 'welcome',
subject: 'Welcome',
template_data: { firstName: 'Jane' },
});
await client.notifications.send_sms({
sender: 'FlexSignal',
to: '+4512345678',
message: 'Alert triggered',
});Available methods:
send_email_template({ sender, to, template_name, subject, template_data?, language_bcp_47? })send_sms({ sender, to, message, transport? })
Geocoding
const geocode = await client.geocode.lookup([
{ identifier: 1, lat: 55.6761, lon: 12.5683 },
]);Error Handling
Most SDK methods return ApiResult<T>:
const result = await session.assets.list();
if (!result.ok) {
console.error(result.error.type, result.error.message);
process.exit(1);
}
console.log(result.data);ApiError.type can be:
fetchhttpparseinvalid_tokenunexpected_state
Authentication methods use AuthResult, which is a discriminated union with the states:
readymfa_requiredinvalid_credentialserror
Exported Types
The package exports the main client classes, endpoint definitions, and response types, including:
FlexsignalClientAuthenticatedSessionflexsignal_endpointsApiResult,ApiError,AuthResult,TokenBundle- schema types such as
AccountData,AssetItem,DeviceItem,GeocodeItem,UnitItem, and storage item types
Development
Scripts:
pnpm format
pnpm lint
pnpm buildThe build output is written to lib/.
