e-bon
e-bon.ro
Referință API

WebSocket de evenimente

Referință de protocol pentru WebSocket-ul de evenimente în timp real al e-bon — URL de conectare, autentificare prin JWT și prin cheie API, tipurile de evenimente, filtrarea după permisiuni, forma cadrului, semnalul de viață, codurile de închidere și strategia de reluare a conexiunii.

WebSocket de evenimente

Backend-ul e-bon expune un singur canal în timp real care livrează evenimentele device.*, receipt.*, command.* și app.* către abonați pe măsură ce se produc.

wss://api.e-bon.ro/ws?subscribe=events

Această pagină este referința de protocol pentru integratorii care se conectează din orice limbaj (Python, Go, PHP, wscat direct, WebSocket din browser etc.).

Alege canalul de livrare

e-bon oferă trei moduri de a afla că s-a întâmplat ceva pe un dispozitiv sau în organizația ta. Nu se exclud reciproc — majoritatea integrărilor de producție folosesc webhook-uri pentru durabilitate și acest WebSocket pentru latență mică în UX.

CanalTransportLatențăDurabilitateFolosit pentru
WebSocket de evenimentePush WSS persistent< 100 msNiciuna — evenimentele emise în timpul deconectării se pierdTablouri de bord live, UX la POS, instrumente de dezvoltare
Webhook-uriPOST HTTPS pe eveniment~ secundeReîncercări durabile cu backoff [60s, 5m, 30m, 2h, 12h]Vezi Webhook-uri
PollingGET /api/v1/...rata interogăriiTras de clientUltima soluție când niciun canal de tip push nu este accesibil

Conectare cu un jeton JWT

Tooling-ul first-party care deține deja un jeton de acces emis de Firebase se conectează cu parametrul de query token:

wss://api.e-bon.ro/ws?subscribe=events&token=<jwt>

Abonații din Portal primesc fiecare eveniment al organizației lor — nu se aplică niciun filtru pe permisiuni.

import WebSocket from 'ws';

const ws = new WebSocket(
  `wss://api.e-bon.ro/ws?subscribe=events&token=${jwt}`
);

ws.on('open', () => console.log('conectat'));
ws.on('message', (raw) => {
  const eveniment = JSON.parse(raw.toString());
  console.log(eveniment.type, eveniment.data);
});
ws.on('close', (code, reason) => {
  console.log('închis', code, reason.toString());
});

Conectare cu o cheie API

Integrările server-to-server care dețin o cheie API ebon_live_* se conectează cu parametrul de query apiKey:

wss://api.e-bon.ro/ws?subscribe=events&apiKey=ebon_live_<cheie>

Abonații parteneri primesc doar evenimentele permise de permisiunile cheii (vezi Filtrare după permisiuni mai jos).

import WebSocket from 'ws';

const ws = new WebSocket(
  `wss://api.e-bon.ro/ws?subscribe=events&apiKey=${apiKey}`
);

ws.on('message', (raw) => {
  const eveniment = JSON.parse(raw.toString());
  console.log(eveniment.type, eveniment.data);
});
Alege exact un singur parametru de autentificare — nu trimite niciodată token și apiKey în aceeași conexiune.

Erori de autentificare

Dacă autentificarea eșuează, sau dacă nu se trimite nici token, nici apiKey, serverul închide conexiunea imediat cu codul 4001 și unul dintre aceste motive de închidere literale:

DeclanșatorMotivul închiderii
JWT invalid sau expiratInvalid or expired token
Cheie API invalidă sau inactivăInvalid or inactive API key
Eroare neașteptată la verificarea cheii APIAuthentication error
Nici token, nici apiKey nu au fost trimiseAuthentication required

Primește evenimente

Fiecare eveniment trimis de server este un singur cadru WebSocket text care conține un singur obiect JSON:

{
  "type": "<tip-eveniment>",
  "data": { /* câmpuri specifice evenimentului */ },
  "timestamp": "2026-04-26T08:09:55.123Z"
}

timestamp este un șir ISO 8601 capturat la momentul livrării. Nu există un plic suplimentar, niciun id, nicio confirmare per eveniment — cadrele sunt de tip „trimite și uită”.

Catalogul de evenimente

Există opt tipuri de evenimente:

typeÎnțelesCâmpuri tipice în data
device.statusStatusul online/offline al unui dispozitiv s-a schimbat.deviceId, status
device.claimedUn dispozitiv anterior nerevendicat a fost atașat organizației tale.deviceId, claimedAt
device.alertA fost ridicată o alertă la nivel de dispozitiv (hârtie scăzută, sertar deschis, eroare).deviceId, severity, message
receipt.createdA fost emis un bon fiscal sau nefiscal.receiptId, deviceId, total, currency
command.completedO comandă din coadă s-a finalizat cu succes pe dispozitiv.commandId, deviceId, result
command.failedO comandă din coadă a eșuat pe dispozitiv.commandId, deviceId, errorCode, errorMessage
app.connectedO instanță a aplicației de pe dispozitiv a deschis un WebSocket către cloud.deviceId, connectedAt
app.disconnectedO instanță a aplicației de pe dispozitiv s-a închis (curat sau prin timeout de heartbeat).deviceId, disconnectedAt, reason

Pentru schemele canonice ale încărcăturilor per eveniment, vezi Evenimente webhook — WebSocket-ul și webhook-urile emit același obiect data pentru tipurile de evenimente care se potrivesc.

Cadre de exemplu

{ "type": "device.status",     "data": { "deviceId": "dev_pos_01", "status": "online" },                                          "timestamp": "2026-04-26T08:09:55.123Z" }
{ "type": "receipt.created",   "data": { "receiptId": "rcp_abc", "deviceId": "dev_pos_01", "total": 4250, "currency": "RON" },    "timestamp": "2026-04-26T08:10:01.456Z" }
{ "type": "command.completed", "data": { "commandId": "cmd_xyz", "deviceId": "dev_pos_01", "result": { "fiscalId": "F00012345" } }, "timestamp": "2026-04-26T08:10:02.789Z" }

Filtrare după permisiuni

Abonații parteneri (autentificare prin cheie API) primesc doar evenimentele al căror type începe cu unul dintre prefixele mapate de pe permisiunile cheii. Abonații din Portal (autentificare JWT) ocolesc acest filtru complet și văd fiecare eveniment al organizației.

Permisiune cheie APIPrefixe permise pentru tipul evenimentului
receiptsreceipt.
receipts:readreceipt.
receipts:adminreceipt.
devicesdevice.
devices:readdevice.
devices:writedevice.
commandscommand.
alldevice., receipt., command., app.

Verificarea este OR pe permisiuni — dacă oricare dintre permisiunile cheii mapează la un prefix cu care începe type-ul evenimentului, evenimentul este livrat. O cheie fără permisiuni recunoscute nu primește nimic.

Evenimentele app.* (app.connected, app.disconnected) sunt accesibile doar prin permisiunea all. Nu există o permisiune mai îngustă mapată la prefixul app..

Endpoint-ul nu are un protocol client→server de subscribe — nu poți restrânge filtrul mai mult decât permite credențialul. Dacă vrei să consumi un singur tip de eveniment, filtrează după eveniment.type în codul clientului.

Pentru catalogul complet de permisiuni, vezi Autentificare API › Permisiuni.

Tratează semnalul de viață

Serverul trimite un cadru ping binar de protocol WebSocket către fiecare abonament activ la fiecare 30 de secunde și așteaptă 10 secunde pong-ul corespunzător. Dacă pong-ul nu sosește la timp, serverul închide conexiunea.

Acesta este ping/pong-ul binar de protocol WebSocket (RFC 6455 §5.5.2 / §5.5.3) — nu un mesaj JSON la nivel de aplicație de tipul {"type":"ping"}. Majoritatea clienților răspund automat:
  • WebSocket din browser — gestionat de browser, nu este expus în JS.
  • Python websockets — răspunde automat. Nu seta ping_interval=None decât dacă implementezi propriul handler de pong.
  • Node ws — răspunde automat.
  • Go gorilla/websocket — apelează SetPongHandler și răspunde din bucla de citire.
  • wscat — răspunde automat.
Dacă scrii un client brut, iar biblioteca ta nu răspunde automat la ping-urile de control, abonamentul tău va fi închis la 10 secunde după prima marcă de 30 de secunde.

Endpoint-ul este doar push după conectare — nu există un mesaj JSON de tip subscribe de la client la server și nicio confirmare per eveniment. Abonații ar trebui să stea tăcuți.

Rămâi sub plafonul de rată

Serverul instalează un plafon de rată per conexiune pentru intrări de 20 de mesaje pe secundă. Mesajele în exces sunt eliminate tăcut — nu se trimite niciun cadru de eroare, iar conexiunea rămâne deschisă. Pentru că protocolul este doar de tip push, clienții normali nu ating niciodată acest plafon.

Reluare conexiune după o deconectare

Serverul nu trimite jetoane de reluare a conexiunii, identificatori de sesiune sau cursori de continuare. Odată ce un client se deconectează (închidere curată, pierdere de rețea, timeout de heartbeat sau eșec de autentificare), evenimentele emise în timp ce clientul era pe dinafară nu sunt redate la următoarea conectare. Folosește Webhook-urile dacă ai nevoie de livrare durabilă.

Strategia recomandată pe partea de client este backoff exponențial cu plafon per încercare:

  • Începe cu o întârziere de 1 secundă după prima deconectare.
  • Dublează întârzierea la fiecare eșec consecutiv (factor 2).
  • Plafonează la 30 de secunde.
  • Resetează întârzierea înapoi la 1 secundă după o conectare reușită.
  • La un close 4001, nu încerca din nou până când operatorul nu rotește credențialul vinovat — un 4001 repetat înseamnă că JWT-ul a expirat sau cheia API a fost dezactivată.
import WebSocket from 'ws';

let delay = 1000;
const max = 30_000;

function connect() {
  const ws = new WebSocket(
    `wss://api.e-bon.ro/ws?subscribe=events&apiKey=${apiKey}`
  );

  ws.on('open', () => { delay = 1000; });
  ws.on('message', (raw) => handle(JSON.parse(raw.toString())));
  ws.on('close', (code) => {
    if (code === 4001) return; // rotește credențialul înainte de reîncercare
    setTimeout(connect, delay);
    delay = Math.min(delay * 2, max);
  });
}

connect();

Coduri de închidere

CodCând
1000Oricare parte a inițiat o închidere curată (de exemplu ai apelat ws.close() curat).
1001Oprire curată a serverului — motivul închiderii Server shutting down.
1006Pierdere de rețea, reset TCP sau serverul a închis conexiunea după un timeout de pong la heartbeat. În acest caz nu se trimite niciun cadru de close.
4001Eșec de autentificare — JWT invalid/expirat, cheie API invalidă/inactivă, autentificare lipsă sau o eroare neașteptată la verificarea cheii API. Vezi motivele închiderii la Erori de autentificare.

Ce urmează

Evenimente webhook

Aceleași încărcături utile, reîncercări durabile, fără conexiune persistentă. Acolo sunt schemele canonice data per eveniment.

Webhook-uri

CRUD pentru abonamentele HTTP de webhook — creare, rotire de secrete, livrări de test, istoric de livrare.

Autentificare API

Catalogul complet de permisiuni pentru cheile API folosit de filtrul de evenimente, plus detalii despre JWT pentru abonații din Portal.