POSfix API Reference
Overview
The POSfix API Server runs locally on the POS device and exposes a REST API on port 4567 (HTTP). It allows an external ERP system to perform fiscal operations — sales, cash-in/cash-out, Z/X reports, periodic reports, and queries — directly on the POS terminal over the local network.
Base URL: http://<device-ip>:4567
Quick Start
- Ensure the POS device and ERP are on the same local network.
- Obtain the device IP from
Settings > Hardwareon the POS app, or from the health endpoint. - Obtain your API Key from the POSfix cloud dashboard.
- Verify connectivity:
GET http://<device-ip>:4567/api/health - Start making fiscal operations with the
X-API-Keyheader.
Authentication
All endpoints (except /api/health) require the X-API-Key header.
X-API-Key: your-api-key-here
The API key is provisioned through the POSfix cloud platform and is unique per terminal.
Missing key → 401 MISSING_API_KEY
Invalid key → 401 INVALID_API_KEY
Response Format
Every response follows this envelope:
{
"success": true,
"data": { ... },
"error": null,
"timestamp": "2026-02-16T12:00:00.000Z"
}
On error:
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description",
"details": [ ... ]
},
"timestamp": "2026-02-16T12:00:00.000Z"
}
| Field | Type | Description |
|---|---|---|
success | boolean | true if the operation succeeded |
data | object | null | Response payload (null on error) |
error | object | null | Error info (null on success) |
error.code | string | Machine-readable error code |
error.message | string | Human-readable error message |
error.details | array | null | Validation error details (field-level) |
timestamp | string | ISO 8601 UTC timestamp |
Endpoints
1. Health & Status
GET /api/health
Check server status. No authentication required.
Returns the app version, fiscal status, terminal ID, device IP, and uptime.
Response fields:
| Field | Type | Description |
|---|---|---|
status | string | Server state: "ready" |
version | string | App version (e.g. "1.0.1+5") |
fiscalStatus | string | Fiscal status: "online", "offline", "not_fiscal" |
terminalId | string | Terminal identifier |
serverPort | int | Listening port (4567) |
uptime | int | Server uptime in seconds |
serverStartedAt | string | ISO 8601 timestamp when server started |
deviceIp | string | Device IP address on local network |
Example response:
{
"success": true,
"data": {
"status": "ready",
"version": "1.0.1+5",
"fiscalStatus": "online",
"terminalId": "T-12345",
"serverPort": 4567,
"uptime": 3600,
"serverStartedAt": "2026-02-16T10:00:00.000Z",
"deviceIp": "192.168.1.50"
},
"error": null,
"timestamp": "2026-02-16T12:00:00.000Z"
}
2. Sales
POST /api/sales
Create a new sale. Performs fiscal registration with MEV (tax authority), prints receipt, and optionally delivers receipt via SMS/email.
Request body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
items | array | Yes | — | Array of sale items (at least 1) |
items[].id | string | No | null | External product ID from ERP. If omitted, sync payload will contain null |
items[].name | string | Yes | — | Product name (1-100 characters) |
items[].unitPrice | number | Yes | — | Unit price (must be > 0) |
items[].quantity | number | Yes | — | Quantity (must be > 0). Decimals allowed if allowDecimals: true |
items[].vatCode | string | Yes | — | VAT code: A, B, C, D, E, or _ |
items[].discountPercent | number | No | 0 | Item-level discount percentage (0-100) |
items[].allowDecimals | boolean | No | false | Allow fractional quantities (e.g. 0.750 kg) |
cartDiscount | object | No | null | Cart-level discount |
cartDiscount.percent | number | Yes | — | Cart discount percentage (0-100) |
payments | array | Yes | — | Array of payments (at least 1) |
payments[].type | string | Yes | — | ACPS payment type code (see Payment Types) |
payments[].amount | number | Yes | — | Payment amount |
payments[].rrn | string | No | null | RRN of a card payment already charged in another app or on another terminal, max. 12 characters. With it, the till does not start the bank terminal and only issues the receipt; without it, the till charges the card itself. A payment sent with an RRN is voided and refunded in the app that charged the card. |
amountReceived | number | Yes | — | Total amount received from customer |
change | number | Yes | — | Change returned to customer |
delivery | object | No | null | Receipt delivery options (SMS/email) |
delivery.customerPhone | string | No | null | Phone number for SMS delivery (format: +XXXXXXXXXXX) |
delivery.customerEmail | string | No | null | Email for receipt delivery |
delivery.customerName | string | No | null | Customer name (max 60 chars) |
delivery.language | string | No | "ro" | Language for receipt: "ro", "en", "ru" |
printReceipt | boolean | No | true | Print receipt on POS printer |
Response fields:
| Field | Type | Description |
|---|---|---|
saleId | string | Local sale UUID |
purchaseId | string | Server-side purchase ID |
serverSaleId | string | Server-side sale ID |
fiscalOperationId | string | Fiscal operation UUID |
receiptNumber | int | Sequential receipt number within current Z report |
reportNumber | int | Current Z report number |
fiscalCode | string | Fiscal code from MEV |
mevId | string | null | MEV transaction ID (null if offline) |
isOffline | boolean | true if MEV was unreachable (offline fiscal receipt) |
offlineFiscalCode | string | null | Offline fiscal code (when operating offline) |
total | number | Final sale total (after discounts) |
subtotal | number | Subtotal before cart discount |
amountReceived | number | Amount received from customer |
change | number | Change returned |
vatBreakdown | object | Per-VAT-code breakdown (see VAT Breakdown) |
paymentBreakdown | array | Array of { type, amount } objects |
qrCodeData | string | QR code URL for receipt verification |
mevResponseXml | string | SOAP XML response from MEV |
receiptDelivery | object | null | { smsSent, emailSent } — indicates delivery was requested, not confirmed |
createdAt | string | ISO 8601 timestamp |
Example — Simple sale (1 item, cash):
// Request
POST /api/sales
{
"items": [
{
"name": "Cafea espresso",
"unitPrice": 45.00,
"quantity": 1,
"vatCode": "B"
}
],
"payments": [
{ "type": "1", "amount": 45.00 }
],
"amountReceived": 45.00,
"change": 0,
"printReceipt": true
}
// Response 200
{
"success": true,
"data": {
"saleId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"purchaseId": "server-purchase-001",
"serverSaleId": "server-sale-001",
"fiscalOperationId": "fiscal-op-001",
"receiptNumber": 1,
"reportNumber": 1,
"fiscalCode": "MEV-TXN-001",
"mevId": "MEV-TXN-001",
"isOffline": false,
"offlineFiscalCode": null,
"total": 45.00,
"subtotal": 45.00,
"amountReceived": 45.00,
"change": 0.00,
"vatBreakdown": {
"B": {
"code": "B",
"percent": 20.0,
"vatAmount": 7.50,
"taxableBase": 37.50,
"totalWithVat": 45.00
}
},
"paymentBreakdown": [
{ "type": "1", "amount": 45.00 }
],
"qrCodeData": "https://sift-mev.sfs.md/verify?id=MEV-TXN-001",
"mevResponseXml": "<?xml version=\"1.0\"?><soap:Envelope>...</soap:Envelope>",
"receiptDelivery": null,
"createdAt": "2026-02-16T12:00:00.000Z"
},
"error": null,
"timestamp": "2026-02-16T12:00:00.000Z"
}
Example — Complex sale (multiple items, mixed payments, discounts, delivery):
// Request
POST /api/sales
{
"items": [
{
"id": "prod-001",
"name": "Cafea espresso",
"unitPrice": 45.00,
"quantity": 2,
"vatCode": "B"
},
{
"name": "Tort Napoleon (kg)",
"unitPrice": 180.00,
"quantity": 0.750,
"vatCode": "B",
"discountPercent": 10.0,
"allowDecimals": true
},
{
"name": "Apa minerala 0.5L",
"unitPrice": 15.00,
"quantity": 3,
"vatCode": "B"
}
],
"cartDiscount": {
"percent": 5.0
},
"payments": [
{ "type": "1", "amount": 200.00 },
{ "type": "2", "amount": 50.00, "rrn": "123456789012" }
],
"amountReceived": 250.00,
"change": 0,
"delivery": {
"customerPhone": "+37369123456",
"customerEmail": "client@email.com",
"customerName": "Vasile Munteanu",
"language": "ro"
},
"printReceipt": true
}
Validation errors return HTTP 400 with field-level details:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "items[0].name",
"code": "ITEM_NAME_INVALID",
"message": "Item name must be 1-100 characters"
},
{
"field": "items[0].unitPrice",
"code": "ITEM_PRICE_INVALID",
"message": "Unit price must be greater than 0"
}
]
},
"timestamp": "2026-02-16T12:00:00.000Z"
}
GET /api/sales
List sales with date range filtering and pagination.
Query parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
dateFrom | string | No | — | Start date (ISO: YYYY-MM-DD) |
dateTo | string | No | — | End date (ISO: YYYY-MM-DD) |
limit | int | No | 50 | Max results per page |
offset | int | No | 0 | Pagination offset |
Response fields:
| Field | Type | Description |
|---|---|---|
sales | array | Array of sale summaries |
sales[].id | string | Sale UUID |
sales[].purchaseId | string | Server purchase ID |
sales[].serverSaleId | string | Server sale ID |
sales[].fiscalOperationId | string | Fiscal operation UUID |
sales[].cashierId | string | Cashier ID |
sales[].cashierName | string | Cashier name |
sales[].subtotal | number | Subtotal amount |
sales[].total | number | Total amount |
sales[].paymentType | string | Primary payment type label |
sales[].paymentAmount | number | Amount paid |
sales[].changeAmount | number | Change returned |
sales[].status | string | Sync status |
sales[].createdAt | string | ISO 8601 timestamp |
total | int | Total number of matching sales |
limit | int | Applied limit |
offset | int | Applied offset |
Example:
GET /api/sales?dateFrom=2026-02-01&dateTo=2026-02-16&limit=50&offset=0
GET /api/sales/{saleId}
Get full details of a specific sale including items, payments, and fiscal operation data.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
saleId | string | Sale UUID |
Response includes:
| Section | Description |
|---|---|
items[] | Product details: productId, productName, quantity, unitPrice, originalPrice, discountAmount, markupAmount, netAmount, vatCode, vatPercent, vatAmount |
payments[] | Payment details: paymentType, amount, notes |
fiscalOperation | Fiscal data: mevId, receiptNumber, reportNumber, status, isOffline, mevResponseXml, qrCodeData |
Error: 404 SALE_NOT_FOUND if the sale ID doesn't exist.
3. Cash Operations
POST /api/cash/in
Deposit cash into the register. Creates a fiscal service receipt sent to MEV.
Request body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
amount | number | Yes | — | Amount to deposit (must be > 0) |
reason | string | No | null | Reason for the deposit |
printReceipt | boolean | No | true | Print service receipt |
Response fields:
| Field | Type | Description |
|---|---|---|
operationId | string | Operation UUID |
fiscalOperationId | string | Fiscal operation UUID |
receiptNumber | int | Service receipt number |
reportNumber | int | Current Z report number |
mevId | string | null | MEV transaction ID |
isOffline | boolean | Whether operation was offline |
amount | number | Deposited amount |
newBalance | number | New cash balance |
operationType | string | "cashIn" |
mevResponseXml | string | SOAP response XML |
createdAt | string | ISO 8601 timestamp |
Example:
// Request
POST /api/cash/in
{
"amount": 500.00,
"reason": "Depunere inceput zi",
"printReceipt": true
}
// Response 200
{
"success": true,
"data": {
"operationId": "op-uuid-001",
"fiscalOperationId": "fiscal-op-003",
"receiptNumber": 1,
"reportNumber": 1,
"mevId": "MEV-SVC-001",
"isOffline": false,
"amount": 500.00,
"newBalance": 500.00,
"operationType": "cashIn",
"mevResponseXml": "...",
"createdAt": "2026-02-16T08:00:00.000Z"
}
}
POST /api/cash/out
Withdraw cash from the register. Verifies sufficient balance before proceeding.
Request body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
amount | number | Yes | — | Amount to withdraw (must be > 0) |
reason | string | No | null | Reason for the withdrawal |
printReceipt | boolean | No | true | Print service receipt |
Response fields: Same as cash-in, with operationType: "cashOut".
Error: 409 INSUFFICIENT_CASH_BALANCE if the current balance is less than the requested amount.
{
"success": false,
"error": {
"code": "INSUFFICIENT_CASH_BALANCE",
"message": "Cash balance (300.00) is insufficient for withdrawal of 500.00"
}
}
4. Reports
POST /api/reports/z
Generate a Z Report — closes the fiscal day.
This is an irreversible operation. It resets receipt counters and increments the report number.
Request body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
printReport | boolean | No | true | Print the report |
Requires: Terminal must be fiscalized and have valid MEV credentials.
Response fields:
| Field | Type | Description |
|---|---|---|
reportNumber | int | Z report number |
operationId | string | Fiscal operation UUID |
mevId | string | null | MEV transaction ID |
isOffline | boolean | Whether operation was offline |
reportData | object | Full report data (see Report Data below) |
mevResponseXml | string | SOAP XML response from MEV |
generatedAt | string | ISO 8601 timestamp |
Report Data object:
| Field | Type | Description |
|---|---|---|
reportNumber | int | Z report number |
dailyTotal | number | Total sales for the day |
dailyTax | number | Total VAT for the day |
dailyUntaxed | number | Untaxed amount |
vatBreakdown | object | Per-VAT-code breakdown |
receiptsIssued | int | Number of receipts issued |
lastReceiptNumber | int | Last receipt number |
receiptsSentToMev | int | Receipts successfully sent to MEV |
paymentTotals | object | Amount per ACPS payment type code (e.g. { "1": 3000.00, "2": 2000.00 }) |
paymentCounts | object | Count per ACPS payment type code (e.g. { "1": 25, "2": 17 }) |
cashIn | number | Total cash deposits |
cashOut | number | Total cash withdrawals |
cashBalance | number | Current cash balance |
yearTotal | number | null | Cumulative yearly total (Z only) |
yearTax | number | null | Cumulative yearly tax (Z only) |
totalSales | number | Total gross sales |
totalReturns | number | Total returns amount |
returnsCount | int | Number of return operations |
cashInCount | int | Number of cash-in operations |
cashOutCount | int | Number of cash-out operations |
generatedAt | string | ISO 8601 timestamp |
POST /api/reports/x
Generate an X Report — intermediate fiscal day snapshot.
Does NOT reset any counters. Same request/response structure as Z Report.
POST /api/reports/periodic
Generate a Periodic Report — aggregated data across multiple Z reports.
This is a local report only (not sent to MEV). Supports two mutually exclusive filter modes.
Request body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
dateFrom | string | Conditional | — | Start date (ISO: YYYY-MM-DD). Required for date range mode |
dateTo | string | Conditional | — | End date (ISO: YYYY-MM-DD). Required for date range mode |
reportFrom | int | Conditional | — | Starting Z report number. For report range mode |
reportTo | int | Conditional | — | Ending Z report number. For report range mode |
detailed | boolean | No | false | Include individual Z report breakdown |
printReport | boolean | No | true | Print the report on POS printer |
Filter modes (mutually exclusive — provide one set):
- Date range:
dateFrom+dateTo - Report number range:
reportFromand/orreportTo(both optional — omitreportFromto start from Z#1, omitreportTofor latest)
Response fields:
| Field | Type | Description |
|---|---|---|
filterMode | string | "dateRange" or "reportRange" |
dateFrom | string | Start date (YYYY-MM-DD) |
dateTo | string | End date (YYYY-MM-DD) |
reportFrom | int | (report range only) Starting Z report number |
reportTo | int | (report range only) Ending Z report number |
totalSales | number | Aggregated total sales |
totalTax | number | Aggregated total VAT |
totalUntaxed | number | Aggregated untaxed amount |
vatBreakdown | object | Per-VAT-code aggregated breakdown |
receiptsCount | int | Total receipts across all Z reports |
paymentTotals | object | Aggregated payment totals by type |
cashIn | number | Total cash deposits |
cashOut | number | Total cash withdrawals |
cashInCount | int | Number of cash-in operations |
cashOutCount | int | Number of cash-out operations |
zReportsCount | int | Number of Z reports in range |
xReportsCount | int | Number of X reports in range |
firstZReportNumber | int | First Z report number in range |
lastZReportNumber | int | Last Z report number in range |
firstZReportTimestamp | string | First Z report close timestamp |
lastZReportTimestamp | string | Last Z report close timestamp |
totalReturns | number | Aggregated returns |
returnsCount | int | Number of return operations |
individualZReports | array | null | Per-Z breakdown (when detailed: true) |
generatedAt | string | ISO 8601 timestamp |
Individual Z Report fields (when detailed: true):
| Field | Type | Description |
|---|---|---|
reportNumber | int | Z report number |
closedAt | string | ISO 8601 timestamp when Z was closed |
firstReceiptNumber | int | First receipt number in this Z |
lastReceiptNumber | int | Last receipt number in this Z |
receiptsCount | int | Number of receipts |
totalSales | number | Total sales for this Z |
totalTax | number | Total VAT for this Z |
vatBreakdown | object | VAT breakdown for this Z |
paymentTotals | object | Payment totals by type |
cashIn | number | Cash deposits in this Z |
cashOut | number | Cash withdrawals in this Z |
Example — Summary by date range:
// Request
POST /api/reports/periodic
{
"dateFrom": "2026-02-01",
"dateTo": "2026-02-16",
"detailed": false,
"printReport": true
}
Example — Summary by Z report number range:
// Request
POST /api/reports/periodic
{
"reportFrom": 5,
"reportTo": 10,
"detailed": false,
"printReport": true
}
Example — Detailed by date range:
// Request
POST /api/reports/periodic
{
"dateFrom": "2026-02-01",
"dateTo": "2026-02-16",
"detailed": true,
"printReport": false
}
Validation errors:
| Code | Condition |
|---|---|
FILTER_REQUIRED | Neither date range nor report range provided |
DATE_RANGE_INVALID | dateFrom is after dateTo |
DATE_TO_FUTURE | dateTo is in the future |
REPORT_NUMBER_INVALID | reportFrom or reportTo < 1 |
REPORT_RANGE_INVALID | reportFrom > reportTo |
5. Fiscal Queries
GET /api/fiscal/operations
Query fiscal operations from the audit database with filtering.
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
dateFrom | string | No | Start date (ISO: YYYY-MM-DD) |
dateTo | string | No | End date (ISO: YYYY-MM-DD) |
type | string | No | Operation type filter (see Reference Tables) |
status | string | No | Operation status filter (see Reference Tables) |
limit | int | No | Max results (default 50) |
offset | int | No | Pagination offset (default 0) |
Response fields:
| Field | Type | Description |
|---|---|---|
operations | array | Array of fiscal operations |
operations[].id | string | Operation UUID |
operations[].operationType | string | Type of fiscal operation |
operations[].receiptNumber | int | Receipt number |
operations[].reportNumber | int | Z report number |
operations[].mevId | string | null | MEV transaction ID |
operations[].status | string | Operation status |
operations[].isOffline | boolean | Whether operated offline |
operations[].totalAmount | number | Operation amount |
operations[].vatBreakdown | object | Simplified VAT breakdown |
operations[].vatDetails | object | Detailed VAT with percent, rate, cost, gross |
operations[].paymentsBreakdown | object | Payments by type |
operations[].mevResponseXml | string | SOAP response XML |
operations[].errorMessage | string | null | Error message if operation failed |
operations[].createdAt | string | ISO 8601 creation timestamp |
operations[].completedAt | string | ISO 8601 completion timestamp |
total | int | Total matching operations |
limit | int | Applied limit |
offset | int | Applied offset |
GET /api/fiscal/daily-summary
Get the current fiscal day summary. Returns the same data as an X Report but without sending anything to MEV. Read-only, no fiscal lock required.
Requires: Terminal must be fiscalized.
Response: Same as Report Data object (see Z Report section).
6. Print
POST /api/print
Print non-fiscal text on the POS thermal printer. Supports multi-column layouts.
Request body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
lines | array | Yes | — | Array of line arrays |
feedLines | int | No | 3 | Number of blank lines after content |
cut | boolean | No | true | Cut paper after printing |
Line format — each line is an array of 1-3 strings:
| Columns | Layout |
|---|---|
["text"] | Full width, left-aligned |
["left", "right"] | Left + right aligned |
["left", "center", "right"] | Three-column layout |
[""] | Empty line (spacer) |
This is a non-fiscal operation — no MEV interaction, no fiscal receipt.
Response fields:
| Field | Type | Description |
|---|---|---|
linesCount | int | Number of lines printed |
printedAt | string | ISO 8601 timestamp |
Example — Inventory report (3 columns):
POST /api/print
{
"lines": [
["RAPORT INVENTAR"],
["Data:", "16.02.2026"],
[""],
["Cod", "Denumire", "Cantitate"],
["────────────────────────────────"],
["001", "Cafea espresso", "150 buc"],
["002", "Tort Napoleon", "12.5 kg"],
["003", "Apa minerala 0.5L", "340 buc"],
[""],
["Total pozitii:", "4"],
["────────────────────────────────"],
["Responsabil:", "", "Ion Popescu"]
],
"feedLines": 3,
"cut": true
}
Error: 503 PRINTER_UNAVAILABLE if the printer is disconnected or not available.
Reference Tables
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
MISSING_API_KEY | 401 | X-API-Key header not provided |
INVALID_API_KEY | 401 | Provided API key is not valid |
VALIDATION_ERROR | 400 | Request body validation failed (see details[]) |
INVALID_JSON | 400 | Request body is not valid JSON |
TERMINAL_NOT_FISCAL | 409 | Terminal is not fiscalized (no MEV certificate) |
MEV_CREDENTIALS_MISSING | 409 | Terminal is fiscalized but MEV credentials are missing |
FISCAL_DAY_EXPIRED | 409 | 24h have passed since the first receipt of the fiscal day — Z Report required before continuing |
INSUFFICIENT_CASH_BALANCE | 409 | Cash balance insufficient for the requested withdrawal |
MEV_ERROR | 502 | Communication with MEV (SFS) failed (timeout, SOAP error, expired certificate) |
BUSY | 429 | Another fiscal operation is in progress (30s timeout exceeded) |
SALE_NOT_FOUND | 404 | Sale with specified ID not found |
PRINTER_UNAVAILABLE | 503 | Printer is not available or disconnected |
VAT Codes
| Code | Description |
|---|---|
A | VAT rate A (as configured on terminal) |
B | VAT rate B (typically 20%) |
C | VAT rate C |
D | VAT rate D |
E | VAT rate E |
_ | Exempt (no VAT) |
Payment Types (ACPS WSDL)
| Type | Description |
|---|---|
"1" | NUMERAR (Cash) |
"2" | CARD |
"3.1" | VAUCHER |
"3.2" | CEC / Certificat valoric |
"3.3" | TICHET — plata cu document de valoare prestabilita (MDL) |
"5" | TME — tichet de masa pe suport electronic |
"6" | ABONAMENT |
"7" | ALT IP (alt instrument de plata) |
"8.1" | Credit |
"8.2" | Leasing |
"8.3" | Avans |
"8.4" | Arvuna |
"8.5" | Gaj |
"8.8" | Compensare |
"8.9" | Alt mod |
Fiscal Operation Types
| Type | Description |
|---|---|
receipt | Fiscal receipt (sale) |
cash_in | Cash deposit |
cash_out | Cash withdrawal |
refund | Refund/return |
x_report | X Report |
z_report | Z Report |
Fiscal Operation Statuses
| Status | Description |
|---|---|
pending | Created, not yet sent to MEV |
sent | Sent to MEV, awaiting response |
success | Successfully processed by MEV |
failed | MEV returned an error |
offline | Stored locally (MEV unreachable) |
VAT Breakdown Object
{
"B": {
"code": "B",
"percent": 20.0,
"vatAmount": 7.50,
"taxableBase": 37.50,
"gross": 45.00
}
}
| Field | Type | Description |
|---|---|---|
code | string | VAT rate code |
percent | number | VAT percentage |
vatAmount | number | VAT amount |
taxableBase | number | Net amount (excluding VAT) |
gross | number | Gross amount (including VAT) |
Notes
- All monetary amounts are in MDL (Moldovan Lei) as
numberwith 2 decimal precision. - All timestamps are in ISO 8601 UTC format.
- The server uses a fiscal operation lock — only one fiscal operation (sale, cash-in/out, Z/X report) can run at a time. Concurrent requests will wait up to 30 seconds before returning
429 BUSY. - Offline mode: When MEV (tax authority server) is unreachable, fiscal operations are stored locally and synced later. The response will have
isOffline: trueandmevId: null. - Receipt printing is fire-and-forget — the API response does not wait for printing to complete.
Postman collections
Two collections, for the two distinct APIs. Import them with Import → File.
| Collection | What's inside | Download |
|---|---|---|
| Terminal API | The cash register API reference, in 28 requests: sales, cash, X/Z and periodic reports, printing, catalog, card voids and refunds, delivery lots. Every request documents its request and response fields, its errors with the HTTP status, and examples. The same API on Android and on PC-ECC (Windows); where they differ, the request's description says so. | posfix-terminal-api.postman_collection.json |
| Public integration API | The 24 requests your external system (online shop, ERP) uses to read and write the merchant's data: catalog, stock, prices, customers, orders, Z reports, webhooks. | posfix-integration-api.postman_collection.json |
Terminal API
Fill in the collection variables:
baseUrl→http://<terminal-ip>:4567on the local network, orhttps://api.posfix.md/api/relay/<publicId>through the relay — the terminal shows this address in the Provider mode windowapiKey→ the key from the terminal (Settings → Developer → Show API key)
Click the collection's name in Postman to see its description: the full error map, the
retry rules with Idempotency-Key, and the reference tables. The whole collection is in
Romanian, including these descriptions.
POST /api/reports/z cannot be undone and resets the day's counters. Don't run it as
a test against a production terminal.
You don't need to open the shift: the terminal opens it on the first sale or cash operation. The day is closed with the Z report.
Public integration API
The key is created in the Control Panel under Integration API keys and shown in
plaintext only once, at creation. Put it in the apiKey variable; it is sent on
every request as X-Posfix-Api-Key.
Each key carries only the scopes chosen at creation — every request's description names
the one it needs. Without it the answer is 403.
The API requires the "API Integrare" module active on the merchant. Without it the
answer is 403 even with a valid key and the right scope.
The merchant is not a parameter: the organisation comes from the key, so one key cannot
read another merchant's data. Start with GET /api/public/v1/ping — it confirms the
key, the merchant and the scopes.