e-bon
e-bon.ro
Realtime

Events

The partner-facing event payloads served on the realtime channel, plus the EventMap / EventName types.

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 stringPayload typeFires when
device.statusDeviceStatusEventA device's connection status changes (online / offline / busy).
device.claimedDeviceClaimedEventA controller (E-BON mobile app instance) claims a device.
device.releasedDeviceReleasedEventA controller releases a device — it becomes unassigned and cloud-only.
device.deletedDeviceDeletedEventAn org admin deletes the device row; the device identity is preserved.
device.alertDeviceAlertEventA device's derived alert set changes (e.g. paper out, offline, Z-report due). An empty alerts array means all clear.
receipt.createdReceiptCreatedEventA receipt is stored on the platform.
command.completedCommandCompletedEventA fiscal command finishes successfully on the printer.
command.failedCommandFailedEventA fiscal command fails on the device. Payload includes a precomputed fiscalError.
command.timeoutCommandTimeoutEventA queued command expires before the device ever answers.
app.connectedAppConnectedEventAn E-BON mobile app instance opens its WebSocket to the server.
app.disconnectedAppDisconnectedEventAn E-BON mobile app instance disconnects.
connectedundefinedThe realtime socket has just (re)opened — local lifecycle only.
disconnectedundefinedThe realtime socket has just dropped — local lifecycle only.
errorErrorThe 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 / sendCommandError API.
  • SDK › Errors — the FiscalError shape attached to command.failed payloads.
  • API › Webhooks — outbound HTTP callbacks if you cannot keep a long-lived WebSocket open.