Events
Events
EventSubscriber wraps the e-bon events WebSocket (wss://api.e-bon.ro/ws) with typed listeners and automatic reconnect. It is the recommended replacement for polling client.commands.get(id) once your integration has more than a handful of devices.
Quick start
The simplest path is to let EBonClient build the subscriber for you, reusing the same credentials:
import { EBonClient } from '@e-bon/sdk';
const client = new EBonClient({
baseUrl: 'https://api.e-bon.ro',
apiKey: process.env.EBON_API_KEY!,
});
const events = client.events.subscribe();
events.on('receipt.created', (data) => {
console.log(`receipt ${data.receiptId} on device ${data.deviceId} = ${data.total}`);
});
events.on('command.failed', (data) => {
// data.fiscalError is a real FiscalError instance — you can throw it directly.
console.error(data.fiscalError);
});
// Later, on shutdown:
events.close();
Construct an EventSubscriber directly
EventSubscriber is also exported on its own and can be constructed directly when you do not have an EBonClient handy (for example in a worker that only listens):
import { EventSubscriber } from '@e-bon/sdk';
const events = new EventSubscriber('https://api.e-bon.ro', {
apiKey: 'ebon_live_...',
});
Signature:
new EventSubscriber(baseUrl: string, auth: { apiKey?: string; token?: string })
baseUrl— the same base URL you giveEBonClient. The subscriber rewrites the protocol tows/wssand appends/ws.auth.token— JWT access token. Sent as?token=...on the WebSocket URL. Takes precedence overauth.apiKeywhen both are set.auth.apiKey— API key. Sent as?apiKey=...when notokenis provided.
The subscriber connects immediately when constructed; you do not need to call a connect() method yourself.
Listen with on, off, once
All three methods are typed by the EventName union derived from EventMap, so the listener payload is inferred for you:
events.on('device.status', (data) => {
// data is DeviceStatusEvent { deviceId, status, timestamp }
});
const handler = (data: ReceiptCreatedEvent) => { /* … */ };
events.on('receipt.created', handler);
events.off('receipt.created', handler); // remove a specific listener
events.once('command.completed', (data) => {
// fires exactly once and unregisters itself
});
Listener errors are swallowed — a throw inside one listener will not prevent other listeners from running, and will not crash the WebSocket loop.
Close the subscriber
Call events.close() when you are done. It marks the subscriber as closed, cancels any pending reconnect timer and tears down the underlying socket. After close() the subscriber is permanent — construct a new one if you need to start over.
Reconnect automatically with exponential backoff
When the socket drops, the subscriber reconnects automatically. The schedule is fixed:
| Constant | Value | Meaning |
|---|---|---|
DEFAULT_RECONNECT_DELAY | 1000 ms | First retry happens 1 second after disconnect. |
RECONNECT_BACKOFF_FACTOR | 2 | Each subsequent retry doubles the delay. |
MAX_RECONNECT_DELAY | 30000 ms | Cap — the delay never grows past 30 seconds. |
So the gaps between reconnect attempts go 1s → 2s → 4s → 8s → 16s → 30s → 30s → … until the socket comes back up, at which point the delay resets to 1 second on the next disconnect. The connected event fires every time the socket reopens; the disconnected event fires every time it drops.
events.on('connected', () => console.log('events: live'));
events.on('disconnected', () => console.warn('events: dropped — reconnecting'));
events.on('error', (err) => console.error('events: socket error', err));
WebSocket. Malformed messages from the server are silently ignored (the socket stays open). If your runtime does not provide WebSocket globally, install a polyfill before importing @e-bon/sdk.Browse the event catalogue
The full set of subscribable events is the EventMap interface re-exported from @e-bon/sdk. The keys below are the exact strings you pass to on / off / once; the payload column is the typed shape of the second argument.
| Event string | Payload type | Fires when |
|---|---|---|
device.status | DeviceStatusEvent | A device's connection status changes (online / offline / busy). |
device.claimed | DeviceClaimedEvent | A controller (E-BON mobile app instance) claims a device. |
receipt.created | ReceiptCreatedEvent | A receipt is stored on the platform. |
command.completed | CommandCompletedEvent | A fiscal command finishes successfully on the printer. |
command.failed | CommandFailedEvent | A fiscal command fails. Payload includes a precomputed fiscalError. |
app.connected | AppConnectedEvent | An E-BON mobile app instance opens its WebSocket to the server. |
app.disconnected | AppDisconnectedEvent | An E-BON mobile app instance disconnects. |
connected | undefined | The events socket has just (re)opened — local lifecycle only. |
disconnected | undefined | The events socket has just dropped — local lifecycle only. |
error | Error | The events socket emitted an error — local lifecycle only. |
The exported event payload types are: DeviceStatusEvent, DeviceClaimedEvent, ReceiptCreatedEvent, CommandCompletedEvent, CommandFailedEvent, CommandTimeoutEvent, AppConnectedEvent and AppDisconnectedEvent. They are all importable as types from @e-bon/sdk. CommandTimeoutEvent is the shape used by the server-side timeout pipeline; on the wire the event is delivered as command.failed with errorCode: ErrorCode.TimeoutCommand, so you handle it through the same listener.
Example — react to receipt.created
A minimal worker that mirrors every receipt into a local audit log:
import { EBonClient, type ReceiptCreatedEvent } from '@e-bon/sdk';
const client = new EBonClient({
baseUrl: 'https://api.e-bon.ro',
apiKey: process.env.EBON_API_KEY!,
});
const events = client.events.subscribe();
events.on('connected', () => console.log('events: live'));
events.on('disconnected', () => console.warn('events: reconnecting…'));
events.on('receipt.created', async (event: ReceiptCreatedEvent) => {
// Pull the full receipt now that we know its id.
const receipt = await client.receipts.get(event.receiptId);
await auditLog.append({
receiptId: receipt.id,
deviceId: event.deviceId,
total: event.total,
timestamp: event.timestamp,
});
});
process.on('SIGTERM', () => events.close());
Where to next
- Errors — the
FiscalErrorshape attached tocommand.failedpayloads. - API › Webhooks — outbound HTTP callbacks if you cannot keep a long-lived WebSocket.
- SDK overview — back to the client surface.
Getting started
A step-by-step walk-through — install @e-bon/sdk, get an API key, instantiate EBonClient, list devices, issue a receipt and handle fiscal errors.
Errors
The FiscalError and EBonApiError exception classes, the ErrorCode enum and RETRYABLE_ERRORS set re-exported from @e-bon/sdk, and a recommended retry pattern.