Events
Events
The realtime channel delivers eleven partner-facing event payloads plus three local-lifecycle events. The keys in the table 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. |
device.released | DeviceReleasedEvent | A controller releases a device — it becomes unassigned and cloud-only. |
device.deleted | DeviceDeletedEvent | An org admin deletes the device row; the device identity is preserved. |
device.alert | DeviceAlertEvent | A device's derived alert set changes (e.g. paper out, offline, Z-report due). An empty alerts array means all clear. |
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 on the device. Payload includes a precomputed fiscalError. |
command.timeout | CommandTimeoutEvent | A queued command expires before the device ever answers. |
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 realtime socket has just (re)opened — local lifecycle only. |
disconnected | undefined | The realtime socket has just dropped — local lifecycle only. |
error | Error | The realtime socket emitted an error — local lifecycle only. |
The exported event payload types are: DeviceStatusEvent, DeviceClaimedEvent, DeviceReleasedEvent, DeviceDeletedEvent, DeviceAlertEvent, ReceiptCreatedEvent, CommandCompletedEvent, CommandFailedEvent, CommandTimeoutEvent, AppConnectedEvent and AppDisconnectedEvent. They are all importable as types from @e-bon/realtime. command.timeout is its own event on the wire — subscribe to it to tell "the printer never answered" apart from command.failed ("the printer refused"), or handle both through the same listener if the distinction doesn't matter to you.
Payload shapes
Every payload is derived from a zod schema exported by the @e-bon/realtime package — schemas are the source of truth and the TypeScript types are inferred from them.
DeviceStatusEvent
{
deviceId: string;
status: DeviceStatus; // 'online' | 'offline' | 'busy' | …
timestamp: string; // ISO-8601
}
DeviceClaimedEvent
{
deviceId: string;
controllerId: string;
controllerName: string;
timestamp: string;
}
DeviceAlertEvent
{
deviceId: string;
alerts: DeviceAlert[];
}
ReceiptCreatedEvent
{
receiptId: string;
deviceId: string;
total: number;
timestamp: string;
}
CommandCompletedEvent
{
commandId: string;
deviceId: string;
type: CommandType;
result: unknown;
timestamp: string;
}
CommandFailedEvent
{
commandId: string;
deviceId: string;
type: CommandType;
error: string;
errorCode: ErrorCode;
retryable: boolean;
timestamp: string;
/** Constructed by the client before dispatch — convenient throwable instance. */
fiscalError: FiscalError;
}
CommandTimeoutEvent
{
id: string;
deviceId: string;
type: CommandType;
error: string;
errorCode: ErrorCode;
retryable: boolean;
timestamp: string;
}
AppConnectedEvent / AppDisconnectedEvent
{
deviceId: string;
timestamp: string;
}
Listen with on, off, once
import { RealtimeClient, type ReceiptCreatedEvent } from '@e-bon/realtime';
const realtime = new RealtimeClient('https://api.e-bon.ro', {
apiKey: process.env.EBON_API_KEY!,
});
realtime.on('device.status', (data) => {
// data: DeviceStatusEvent { deviceId, status, timestamp }
});
const handler = (data: ReceiptCreatedEvent) => { /* … */ };
realtime.on('receipt.created', handler);
realtime.off('receipt.created', handler); // remove a specific listener
realtime.once('command.completed', (data) => {
// fires exactly once and unregisters itself
});
Example — react to receipt.created
A minimal worker that mirrors every receipt into a local audit log via the REST SDK:
import { EBonClient } from '@e-bon/sdk';
import { RealtimeClient, type ReceiptCreatedEvent } from '@e-bon/realtime';
const client = new EBonClient({
baseUrl: 'https://api.e-bon.ro',
apiKey: process.env.EBON_API_KEY!,
});
const realtime = new RealtimeClient('https://api.e-bon.ro', {
apiKey: process.env.EBON_API_KEY!,
});
realtime.on('connected', () => console.log('realtime: live'));
realtime.on('disconnected', () => console.warn('realtime: reconnecting…'));
realtime.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', () => realtime.close());
Where to next
- Reconnect and lifecycle — backoff schedule,
close,suspend/resume,CursorStore. - Schemas — the underlying wire frames if you build a custom client, plus the device→server
send/sendCommandResult/sendCommandErrorAPI. - SDK › Errors — the
FiscalErrorshape attached tocommand.failedpayloads. - API › Webhooks — outbound HTTP callbacks if you cannot keep a long-lived WebSocket open.