e-bon
e-bon.ro
Getting Started

Developer quickstart

POS integrator quickstart — go from zero to your first fiscal command and your first event in about ten minutes, using curl or the @e-bon/sdk Node client.

Developer quickstart

This page is for developers integrating a POS, ERP or back-office tool with e-bon. By the end of it you will have an API key, a successful GET /api/v1/devices call, your first queued print_receipt command and a minimal subscriber listening for the matching command.completed / command.failed event. Plan on roughly ten minutes once you have a verified e-bon organization.

If you are a business owner who just wants to connect a fiscal printer to a phone, the Integration walkthrough is the page for you — this one assumes you can run curl or node from a terminal and read JSON.

There is no separate sandbox tenant today — the steps below use real production keys issued against a free-tier organization. Free tier covers up to 3 devices, has no credit-card requirement, and is the recommended way to develop and test against e-bon. A dedicated sandbox environment is tracked under REC-0bd and will land with its own credentials when ready.

Sign up and copy your first API key

Create an organization in the Cloud Portal (app.e-bon.ro) using your business email. Your CUI is fetched automatically from ANAF — confirm it, set a password, and the organization is provisioned on the free plan with no card required.

Once you land on the dashboard, open Settings → API Keys (path: /portal/api-keys), click Create key, give it a label like Local dev — quickstart, pick at least the devices:read and commands scopes, and copy the key. The full key is shown only once — store it in a password manager or in a .env file you do not commit.

API keys have the shape:

ebon_live_<orgId>_<32-hex>

For the rest of this page, export it once and reuse it:

export EBON_API_KEY="ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"

The portal walkthrough — including rotation, deactivation and last-used tracking — lives at Portal › API keys.

Make your first call — list devices

The smallest end-to-end call you can make is GET /api/v1/devices. It validates your key, returns the devices registered against your organization, and works against an empty organization too (you get back []).

curl

curl https://api.e-bon.ro/api/v1/devices \
  -H "Authorization: Bearer $EBON_API_KEY"

Node — @e-bon/sdk

First install the SDK in your project:

pnpm add @e-bon/sdk

Then:

import { EBonClient } from '@e-bon/sdk';

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

const devices = await client.devices.list();
console.log(devices);

A successful response is a flat JSON array (no envelope):

[
  {
    "id": "dev_pos_01",
    "name": "Tejghea POS 1",
    "protocol": "datecs_compact",
    "transport": "tcp",
    "locationId": "loc_main",
    "connectionParams": { "host": "192.168.1.50", "port": 9100 },
    "status": "online",
    "orgId": "acme_corp",
    "controllerId": "instance_a",
    "controllerName": "Counter A",
    "createdAt": "2026-04-09T08:10:00.000Z",
    "updatedAt": "2026-04-09T08:10:00.000Z"
  }
]

If you have not paired a fiscal device yet, the array will be empty — that is fine for the next step, but you will need at least one online device (a live AMEF, or a development device pointed at a printer simulator) before your print_receipt command can actually complete. The Integration walkthrough covers the pairing flow in the portal and the E-BON Android app.

Pick a deviceId from the response (or register one first) and export it for the next step:

export EBON_DEVICE_ID="dev_pos_01"

Send your first fiscal command

The fiscal-command API is asynchronous: POST /api/v1/commands queues the command, returns 201 with status: "pending" and dispatches it to the device over WebSocket in the background. You then either poll GET /api/v1/commands/{commandId} or — better — subscribe to command.* events (next step).

The smallest valid print_receipt payload is one item paid in cash:

curl

curl -X POST https://api.e-bon.ro/api/v1/commands \
  -H "Authorization: Bearer $EBON_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: quickstart-$(date +%s)" \
  -d '{
    "deviceId": "'"$EBON_DEVICE_ID"'",
    "type": "print_receipt",
    "payload": {
      "items": [
        { "name": "Item A", "price": 10, "quantity": 1, "vatRate": 21 }
      ],
      "payments": [
        { "type": "cash", "amount": 10 }
      ]
    }
  }'

Node — @e-bon/sdk

const command = await client.commands.send({
  deviceId: process.env.EBON_DEVICE_ID!,
  type: 'print_receipt',
  payload: {
    items: [{ name: 'Item A', price: 10, quantity: 1, vatRate: 21 }],
    payments: [{ type: 'cash', amount: 10 }],
  },
});

console.log(command.id, command.status); // → "cmd_…", "pending"

You get back a 201 Created with the queued record:

{
  "id": "cmd_abc123",
  "deviceId": "dev_pos_01",
  "type": "print_receipt",
  "payload": {
    "items": [{ "name": "Item A", "price": 10, "quantity": 1, "vatRate": 21 }],
    "payments": [{ "type": "cash", "amount": 10 }]
  },
  "orgId": "acme_corp",
  "status": "pending",
  "apiKeyId": "apikey_abc",
  "deviceOnline": true,
  "createdAt": "2026-04-09T08:10:00.000Z",
  "updatedAt": "2026-04-09T08:10:00.000Z"
}
A 201 only confirms that the command was persisted and queued — the AMEF can still reject it (failed) or never reply (timeout). Always check the final status, either by polling GET /api/v1/commands/{commandId} or by listening for the command.completed / command.failed events in the next step.

The full per-type payload schemas, the commandRateLimit (50 / 10 min) and the Idempotency-Key header semantics are documented on Commands.

Subscribe to your first event

Polling works, but the real-time path is simpler: open one WebSocket subscriber, listen for command.completed and command.failed, and resolve every command from a single handler.

curl — there is no curl path for the events stream; the channel is a WebSocket. Either jump to the Node snippet below or, while you wait, poll the command record:

curl https://api.e-bon.ro/api/v1/commands/cmd_abc123 \
  -H "Authorization: Bearer $EBON_API_KEY"

Node — @e-bon/sdk

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('command.completed', (data) => {
  // data: { commandId, deviceId, type, result, timestamp }
  console.log(`${data.type} ${data.commandId} on ${data.deviceId} ok`);
});

events.on('command.failed', (data) => {
  // data: { commandId, deviceId, type, error, errorCode, retryable, timestamp, fiscalError }
  console.error(
    `${data.type} ${data.commandId} failed: ${data.errorCode}${data.error} (retryable=${data.retryable})`,
  );
});

// On shutdown:
// events.close();

errorCode is one of the ErrorCode constants — common values include E300 (fiscal memory full), E304 (fiscal hardware error), E400 (validation — invalid payload), E500 (timeout — command), E900 (unknown). The full catalogue and the retryable semantics are documented on API › Webhooks and SDK › Events.

The subscriber reconnects automatically with exponential backoff, so you can leave it running for the lifetime of your worker.

Continue your integration

You have the loop end-to-end: key → list → command → event. Everything else in the e-bon API is a variation on the same shape.

Full API reference

Every REST endpoint, the error envelope, rate limits, idempotency and pagination — start at the API overview, then drill into Devices, Commands, Receipts, Reports and Webhooks.

SDK overview

The full @e-bon/sdk surface — EBonClient, the ten resource accessors, EBonApiError, FiscalError and the typed EventSubscriber.

Integration walkthrough

The portal-and-app version of the same flow, for the business owner side of the integration — accounts, locations, pairing the AMEF, the first X report.

Postman collection

Importable Postman collection — every request from this page, pre-wired with Authorization: Bearer {{EBON_API_KEY}}. See API › Postman collection for the import walkthrough.