Devices
Devices
The Devices API is the largest surface in e-bon. It splits into two layers, both mounted under /api/v1/devices:
- CRUD & introspection — register, update and delete devices, list them, fetch status and alerts. These endpoints read and write the Firestore device record and the in-memory WebSocket gateway.
- Commands & live operations — claim/release control of a device, dispatch fiscal commands (
set_datetime,print_duplicate,non_fiscal_receipt,set_logo, VAT/header/footer/operator configuration, voiding, reversals…), and fetch live data from the device. For most command routes, the platform forwards the request to the device over WebSocket and waits for the device's reply before responding.
Each endpoint declares its own scope below. The four scopes used in this group are devices:read, devices:write, commands and receipts — see Authentication › Choose scopes for the full catalogue. The error envelope, rate limits and authentication rules are documented once on API overview.
503 SERVICE_UNAVAILABLE.CRUD
POST /api/v1/devices
Registers a new fiscal device on the organization.
- Auth scope:
devices:write - Tier check: the request is checked against your plan's device limit before the body is validated; if your organization is already at its device limit, the request is rejected with
403.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | 1–255 chars. |
protocol | string | yes | One of datecs_compact, datecs_professional, datecs_extended, daisy, daisy_ro, eltrade, incotex, tremol, tremol_v2, custom, mfje. |
transport | string | yes | One of tcp, bluetooth, serial, usb. |
locationId | string | yes | The location the device belongs to (≥ 1 char). |
connectionParams | object | yes | Either { host: string, port: number (1–65535) } (TCP) or { address: string } (bluetooth/serial/usb). |
Response (201 Created)
{
"id": "dev_pos_01",
"name": "Tejghea POS 1",
"protocol": "datecs_compact",
"transport": "tcp",
"locationId": "loc_main",
"connectionParams": { "host": "192.168.1.50", "port": 9100 },
"orgId": "acme_corp",
"status": "offline",
"controllerId": null,
"controllerName": null,
"createdAt": "2026-04-09T08:10:00.000Z",
"updatedAt": "2026-04-09T08:10:00.000Z"
}
Example
curl -X POST https://api.e-bon.ro/api/v1/devices \
-H "x-api-key: ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
-H "Content-Type: application/json" \
-d '{
"name": "Tejghea POS 1",
"protocol": "datecs_compact",
"transport": "tcp",
"locationId": "loc_main",
"connectionParams": { "host": "192.168.1.50", "port": 9100 }
}'
Error codes
VALIDATION_ERROR(400) — body failed schema validation.FORBIDDEN(403) — device limit reached for the organization's tier.UNAUTHORIZED/FORBIDDEN— see Authentication › Handle auth errors.
GET /api/v1/devices
Lists devices for the organization, optionally filtered by status or locationId.
- Auth scope:
devices:read
Query parameters
| Parameter | Type | Notes |
|---|---|---|
status | string | One of online, offline, busy, error. |
locationId | string | Filter by location. |
Response (200 OK)
[
{
"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"
}
]
Example
curl "https://api.e-bon.ro/api/v1/devices?status=online" \
-H "x-api-key: ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
GET /api/v1/devices/statuses
Returns the WebSocket connectivity (wsConnected) and Firestore status for every device in the organization, in a single call.
- Auth scope:
devices:read
Response (200 OK)
{
"statuses": {
"dev_pos_01": {
"wsConnected": true,
"firestoreStatus": "online",
"lastSeen": "2026-04-09T08:09:55.000Z"
},
"dev_pos_02": { "wsConnected": false, "firestoreStatus": "offline", "lastSeen": null }
}
}
Example
curl https://api.e-bon.ro/api/v1/devices/statuses \
-H "x-api-key: ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
GET /api/v1/devices/alerts
Returns derived alerts for all devices in the organization, keyed by deviceId.
- Auth scope:
devices:read
Query parameters
| Parameter | Type | Notes |
|---|---|---|
severity | string | Filter by alert severity: warning, error, info. |
Response (200 OK)
{
"alerts": {
"dev_pos_01": [
{
"type": "paper_low",
"severity": "warning",
"message": "Paper roll is below 10%",
"deviceId": "dev_pos_01",
"deviceName": "Tejghea POS 1",
"detectedAt": "2026-04-09T08:00:00.000Z"
}
]
}
}
Example
curl "https://api.e-bon.ro/api/v1/devices/alerts?severity=error" \
-H "x-api-key: ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
GET /api/v1/devices/{deviceId}
Returns a single device by ID.
- Auth scope:
devices:read
Example
curl https://api.e-bon.ro/api/v1/devices/dev_pos_01 \
-H "x-api-key: ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
Error codes
NOT_FOUND(404) — device does not exist in your organization.
PATCH /api/v1/devices/{deviceId}
Updates name, connectionParams and/or locationId. At least one field must be provided.
- Auth scope:
devices:write
Request body
| Field | Type | Notes |
|---|---|---|
name | string | 1–255 chars. |
connectionParams | object | Same shape as on create. |
locationId | string | Move the device to a different location. |
Example
curl -X PATCH https://api.e-bon.ro/api/v1/devices/dev_pos_01 \
-H "x-api-key: ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
-H "Content-Type: application/json" \
-d '{ "name": "Tejghea POS 1 (renumită)" }'
Error codes
VALIDATION_ERROR(400) — body failed schema validation (e.g. empty body).NOT_FOUND(404) — device does not exist in your organization.
DELETE /api/v1/devices/{deviceId}
Permanently removes a device.
- Auth scope:
devices:write - Response:
204 No Contenton success.
Example
curl -X DELETE https://api.e-bon.ro/api/v1/devices/dev_pos_01 \
-H "x-api-key: ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
Error codes
NOT_FOUND(404) — device does not exist in your organization.
Control & status
POST /api/v1/devices/{deviceId}/claim
Sets controllerId and controllerName on a device, indicating which app instance is currently driving it. Broadcasts a device.claimed event on the org events WebSocket.
- Auth scope:
devices:write
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
controllerId | string | yes | 1–255 chars. Stable per instance. |
controllerName | string | yes | 1–255 chars. Human-readable label. |
Example
curl -X POST https://api.e-bon.ro/api/v1/devices/dev_pos_01/claim \
-H "x-api-key: ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
-H "Content-Type: application/json" \
-d '{ "controllerId": "instance_a", "controllerName": "Counter A" }'
Error codes
VALIDATION_ERROR(400) — body failed schema validation.NOT_FOUND(404) — device does not exist.CONFLICT(409) — device is already controlled by a differentcontrollerId.
POST /api/v1/devices/{deviceId}/release
Removes controllerId / controllerName. Only the current controller may release the device.
- Auth scope:
devices:write
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
controllerId | string | yes | The controllerId of the instance releasing control. |
Error codes
FORBIDDEN(403) —controllerIddoes not match the current controller.NOT_FOUND(404) — device does not exist.
GET /api/v1/devices/{deviceId}/status
Returns WebSocket connectivity and Firestore status for one device.
- Auth scope:
devices:read
Response (200 OK)
{
"deviceId": "dev_pos_01",
"wsConnected": true,
"firestoreStatus": "online",
"lastSeen": "2026-04-09T08:09:55.000Z"
}
Error codes
NOT_FOUND(404) — device does not exist.
GET /api/v1/devices/{deviceId}/connection-history
Returns the last 20 connection / disconnection events for a device, newest-first.
- Auth scope:
devices:read
Response (200 OK)
{
"events": [
{
"type": "connected",
"timestamp": "2026-04-09T08:00:00.000Z",
"deviceModel": "Datecs DP-25",
"appVersion": "2.4.1",
"osVersion": "Android 14",
"code": null,
"reason": null
},
{
"type": "disconnected",
"timestamp": "2026-04-08T22:15:00.000Z",
"deviceModel": "Datecs DP-25",
"appVersion": "2.4.1",
"osVersion": "Android 14",
"code": 1006,
"reason": "network unreachable"
}
]
}
Error codes
NOT_FOUND(404) — device does not exist.
GET /api/v1/devices/{deviceId}/alerts
Returns derived alerts for one device.
- Auth scope:
devices:read
Query parameters
| Parameter | Type | Notes |
|---|---|---|
severity | string | Filter by alert severity: warning, error, info. |
Error codes
NOT_FOUND(404) — device does not exist.
Commands
POST /api/v1/devices/{deviceId}/commands
Generic shortcut to enqueue a fiscal command targeting a specific device. The body is pre-validated against the command-payload schema for the given type before dispatch.
- Auth scope:
commands - Rate limit: this route is subject to a stricter command-specific rate limit, in addition to the global API rate limit.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
type | string | yes | One of: print_receipt, void_receipt, x_report, z_report, cash_in, cash_out, get_status, get_cash_amount, set_datetime, print_duplicate, non_fiscal_receipt, set_logo, delete_logo, raw_command, open_drawer (and the more specific types listed in the dedicated routes below). |
payload | object | yes | Command-specific payload. Pre-validated server-side. |
Response (201 Created)
{
"id": "cmd_abc123",
"deviceId": "dev_pos_01",
"type": "print_receipt",
"payload": { "items": [], "payments": [] },
"orgId": "acme_corp",
"status": "pending",
"apiKeyId": "apikey_abc",
"createdAt": "2026-04-09T08:10:00.000Z",
"updatedAt": "2026-04-09T08:10:00.000Z"
}
Error codes
VALIDATION_ERROR(400) — invalid command payload.NOT_FOUND(404) — device does not exist.RATE_LIMIT_EXCEEDED(429) — command rate limit hit.
GET /api/v1/devices/{deviceId}/cash-balance
Dispatches a get_cash_amount command and returns the device's current cash balance.
- Auth scope:
devices:read
Response (200 OK)
{
"cashBalance": 1234.5,
"currency": "RON",
"deviceId": "dev_pos_01",
"timestamp": "2026-04-09T08:10:00.000Z"
}
Error codes
NOT_FOUND(404) — device does not exist.INTERNAL_ERROR(500) — device responded but did not return cash data.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
POST /api/v1/devices/{deviceId}/set-datetime
Dispatches a set_datetime command. If datetime is omitted, the server's current time is used.
- Auth scope:
devices:write
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
datetime | string | no | ISO 8601 string. Defaults to server time. |
Response (200 OK)
{
"success": true,
"datetime": "2026-04-09T08:10:00.000Z",
"deviceId": "dev_pos_01",
"timestamp": "2026-04-09T08:10:00.000Z"
}
Error codes
NOT_FOUND(404) — device does not exist.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
POST /api/v1/devices/{deviceId}/print-duplicate
Dispatches a print_duplicate command to reprint the last receipt. No request body.
- Auth scope:
devices:write
Error codes
NOT_FOUND(404) — device does not exist.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
POST /api/v1/devices/{deviceId}/non-fiscal
Prints a non-fiscal text receipt (free-text, bypasses fiscal logic). Useful for internal notes, training, vouchers etc.
- Auth scope:
devices:write
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
lines | string | yes | At least one line. Each line ≤ 48 chars. |
header | string | no | Optional header text printed bold/centered at the top. ≤ 48 chars. |
Example
curl -X POST https://api.e-bon.ro/api/v1/devices/dev_pos_01/non-fiscal \
-H "x-api-key: ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
-H "Content-Type: application/json" \
-d '{
"header": "MULȚUMIM",
"lines": ["Cod cupon: ABC-123", "Valabil până la 30.06.2026"]
}'
Error codes
VALIDATION_ERROR(400) — body failed schema validation.NOT_FOUND(404) — device does not exist.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
POST /api/v1/devices/{deviceId}/logo
Uploads a base64-encoded receipt logo (BMP/PNG/JPEG depending on the device).
- Auth scope:
devices:write
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
logo | string | yes | Base64-encoded image data, ≥ 1 char. |
Error codes
VALIDATION_ERROR(400) — body failed schema validation.NOT_FOUND(404) — device does not exist.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
DELETE /api/v1/devices/{deviceId}/logo
Removes the currently stored receipt logo on the device. No request body.
- Auth scope:
devices:write
Error codes
NOT_FOUND(404) — device does not exist.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
VAT configuration
GET /api/v1/devices/{deviceId}/vat-rates
Queries the VAT rates currently programmed on the device.
- Auth scope:
devices:read
Response (200 OK)
{
"rates": [
{ "name": "Standard", "percentage": 21 },
{ "name": "Redusă alimente", "percentage": 9 }
],
"deviceId": "dev_pos_01",
"timestamp": "2026-04-09T08:10:00.000Z"
}
Error codes
NOT_FOUND(404) — device does not exist.INTERNAL_ERROR(500) — device responded but did not return rates data.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
GET /api/v1/devices/{deviceId}/vat-capabilities
Queries the device's VAT-rate capabilities (max number of rates, whether names are programmable, etc.).
- Auth scope:
devices:read
Error codes
NOT_FOUND(404) — device does not exist.INTERNAL_ERROR(500) — device did not return capabilities data.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
POST /api/v1/devices/{deviceId}/vat-rates
Reprograms the VAT rates on the device.
- Auth scope:
devices:write
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
rates | array | yes | At least one entry. Each { name: string (≥ 1 char), percentage: number (≥ 0) }. |
Error codes
VALIDATION_ERROR(400) — body failed schema validation.NOT_FOUND(404) — device does not exist.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
Header / footer / operator
GET /api/v1/devices/{deviceId}/header-footer-capabilities
Queries the device's header/footer capabilities (max lines, line length, etc.).
- Auth scope:
devices:read
Error codes
NOT_FOUND(404) — device does not exist.INTERNAL_ERROR(500) — device did not return capabilities data.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
GET /api/v1/devices/{deviceId}/header-footer
Returns the current header and footer programmed on the device.
- Auth scope:
devices:read
Response (200 OK)
{
"header": ["ACME SRL", "Strada Exemplu 1, Bucuresti"],
"footer": ["www.acme.ro", "Vă mulțumim!"],
"deviceId": "dev_pos_01",
"timestamp": "2026-04-09T08:10:00.000Z"
}
Error codes
NOT_FOUND(404) — device does not exist.INTERNAL_ERROR(500) — device did not return header/footer data.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
POST /api/v1/devices/{deviceId}/header-footer
Reprograms the header and footer printed on every receipt.
- Auth scope:
devices:write
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
header | string | yes | Up to 10 lines, each ≤ 48 chars. |
footer | string | yes | Up to 10 lines, each ≤ 48 chars. |
Error codes
VALIDATION_ERROR(400) — body failed schema validation.NOT_FOUND(404) — device does not exist.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
GET /api/v1/devices/{deviceId}/operator-capabilities
Queries the device's operator capabilities (max number of operators, whether names/passwords are programmable, etc.).
- Auth scope:
devices:read
Error codes
NOT_FOUND(404) — device does not exist.INTERNAL_ERROR(500) — device did not return capabilities data.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
POST /api/v1/devices/{deviceId}/operator
Programs an operator (cashier) slot on the device.
- Auth scope:
devices:write
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
operatorId | integer | yes | Operator slot, ≥ 1. |
name | string | yes | 1–32 chars. |
password | string | no | ≤ 8 chars. |
Error codes
VALIDATION_ERROR(400) — body failed schema validation.NOT_FOUND(404) — device does not exist.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
Live data & corrective actions
GET /api/v1/devices/{deviceId}/info
Dispatches a get_info command and returns the device's identifying information (model, firmware, fiscal series, etc., as exposed by the protocol).
- Auth scope:
devices:read
Error codes
NOT_FOUND(404) — device does not exist.INTERNAL_ERROR(500) — device did not return info data.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
GET /api/v1/devices/{deviceId}/last-receipt
Dispatches a get_last_receipt_info command and returns the device's last receipt summary.
- Auth scope:
devices:read
Error codes
NOT_FOUND(404) — device does not exist.INTERNAL_ERROR(500) — device did not return last-receipt data.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
POST /api/v1/devices/{deviceId}/void-open
Dispatches a void_open_receipt command to discard a receipt that is open on the AMEF (e.g. after an aborted sale). No request body.
- Auth scope:
devices:write
Response (200 OK)
{
"success": true,
"message": "Open receipt voided successfully",
"deviceId": "dev_pos_01",
"timestamp": "2026-04-09T08:10:00.000Z"
}
Error codes
NOT_FOUND(404) — device does not exist.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
POST /api/v1/devices/{deviceId}/reversal
Dispatches a print_reversal_receipt command. The body is forwarded as the command payload after server-side validation against the reversal payload schema. Returns the printed reversal's receiptNumber when the device reports it.
- Auth scope:
devices:write
Response (201 Created)
{
"success": true,
"message": "Reversal receipt printed successfully",
"receiptNumber": "0000123",
"deviceId": "dev_pos_01",
"timestamp": "2026-04-09T08:10:00.000Z"
}
Error codes
VALIDATION_ERROR(400) — payload failed reversal-receipt validation.NOT_FOUND(404) — device does not exist.SERVICE_UNAVAILABLE(503) — device offline or no controller assigned.
GET /api/v1/devices/{deviceId}/receipts
Shortcut to list receipts filtered by device, newest-first. This is a convenience wrapper around GET /receipts.
- Auth scope:
receipts
Query parameters
| Parameter | Type | Default | Notes |
|---|---|---|---|
type | string | — | Filter by receipt type (sale / refund / storno). |
limit | integer | 50 | 1–100. |
Example
curl "https://api.e-bon.ro/api/v1/devices/dev_pos_01/receipts?type=sale&limit=20" \
-H "x-api-key: ebon_live_acme_corp_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
Error codes
VALIDATION_ERROR(400) — bad query.NOT_FOUND(404) — device does not exist.
See also
- Receipts — store and search the receipts produced by these devices.
- Reports — X / Z / JE / MF reports built on top of the receipt journal.
- Authentication — scope catalogue used by every endpoint above.
- API overview — base URL, error envelope, rate limits, idempotency, pagination.
Reports
REST endpoints for fiscal reports — X (running totals), Z (end-of-day), JE (ANAF Electronic Journal XML) and MF (Fiscal Memory archive) — with request/response schemas, curl examples and per-endpoint error codes.
Webhooks
REST endpoints for managing webhook subscriptions — create, list, update, delete, rotate signing secrets, send test deliveries, and inspect delivery history.