e-bon
e-bon.ro
Realtime

Getting started

Install @e-bon/realtime, instantiate the RealtimeClient with an API key or JWT, and register your first event listener.

Getting started

This page takes you from pnpm add to a live listener in under a minute. If you do not have an API key yet, the Developer quickstart walks through creating an organization and copying one.

Install

pnpm add @e-bon/realtime

npm install @e-bon/realtime and yarn add @e-bon/realtime work too. The package has a single runtime dependency on @e-bon/types and ships with TypeScript declarations.

RealtimeClient uses the runtime's native WebSocket. On Node it requires Node 22 or newer. No polyfill is bundled — install one globally before importing @e-bon/realtime if your runtime does not expose WebSocket.

Instantiate the client

RealtimeClient takes a baseUrl and a RealtimeClientOptions object with one of two auth modes — never both at once. The constructor connects immediately; there is no connect() method.

With an API key (POS partners, server integrations)

import { RealtimeClient } from '@e-bon/realtime';

const realtime = new RealtimeClient('https://api.e-bon.ro', {
  apiKey: process.env.EBON_API_KEY!,
});

The client rewrites https:// → wss://, appends /ws and sends the API key as the apiKey query parameter.

With a JWT (portal sessions, the mobile app)

import { RealtimeClient } from '@e-bon/realtime';

const realtime = new RealtimeClient('https://api.e-bon.ro', {
  token: 'eyJ...',
});

The token is sent as the token query parameter and takes precedence over apiKey when both are set.

Options

OptionTypeDefaultMeaning
apiKeystring—API key for POS partner authentication. Mutually exclusive with token.
tokenstring—JWT access token for portal authentication. Mutually exclusive with apiKey.
cursorStoreCursorStore—Optional persistence hook for the last-seen event cursor. See Reconnect and lifecycle.

If you pass neither apiKey nor token, the gateway will reject the WebSocket handshake with a 401.

Register your first listener

on / off / once are typed by the EventName union derived from EventMap, so the listener payload is inferred for you:

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('connected', () => console.log('realtime: live'));
realtime.on('disconnected', () => console.warn('realtime: reconnecting…'));

realtime.on('receipt.created', (event: ReceiptCreatedEvent) => {
  console.log(`receipt ${event.receiptId} on device ${event.deviceId} = ${event.total}`);
});

realtime.on('command.failed', (event) => {
  // event.fiscalError is a real FiscalError instance — you can throw it directly.
  console.error(event.fiscalError);
});

// Later, on shutdown:
process.on('SIGTERM', () => realtime.close());

Listener errors are swallowed — a throw inside one listener will not prevent other listeners from running and will not crash the WebSocket loop.

Where to next

  • Events — the full catalogue of subscribable events.
  • Reconnect and lifecycle — backoff schedule, suspend / resume, and the CursorStore hook.
  • Schemas — wire-protocol frames if you build a custom client.