Skip to main content

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

  1. Ensure the POS device and ERP are on the same local network.
  2. Obtain the device IP from Settings > Hardware on the POS app, or from the health endpoint.
  3. Obtain your API Key from the POSfix cloud dashboard.
  4. Verify connectivity:
    GET http://<device-ip>:4567/api/health
  5. Start making fiscal operations with the X-API-Key header.

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 key401 MISSING_API_KEY Invalid key401 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"
}
FieldTypeDescription
successbooleantrue if the operation succeeded
dataobject | nullResponse payload (null on error)
errorobject | nullError info (null on success)
error.codestringMachine-readable error code
error.messagestringHuman-readable error message
error.detailsarray | nullValidation error details (field-level)
timestampstringISO 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:

FieldTypeDescription
statusstringServer state: "ready"
versionstringApp version (e.g. "1.0.1+5")
fiscalStatusstringFiscal status: "online", "offline", "not_fiscal"
terminalIdstringTerminal identifier
serverPortintListening port (4567)
uptimeintServer uptime in seconds
serverStartedAtstringISO 8601 timestamp when server started
deviceIpstringDevice 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:

FieldTypeRequiredDefaultDescription
itemsarrayYesArray of sale items (at least 1)
items[].idstringNonullExternal product ID from ERP. If omitted, sync payload will contain null
items[].namestringYesProduct name (1-100 characters)
items[].unitPricenumberYesUnit price (must be > 0)
items[].quantitynumberYesQuantity (must be > 0). Decimals allowed if allowDecimals: true
items[].vatCodestringYesVAT code: A, B, C, D, E, or _
items[].discountPercentnumberNo0Item-level discount percentage (0-100)
items[].allowDecimalsbooleanNofalseAllow fractional quantities (e.g. 0.750 kg)
cartDiscountobjectNonullCart-level discount
cartDiscount.percentnumberYesCart discount percentage (0-100)
paymentsarrayYesArray of payments (at least 1)
payments[].typestringYesACPS payment type code (see Payment Types)
payments[].amountnumberYesPayment amount
payments[].rrnstringNonullRetrieval Reference Number (for card payments)
amountReceivednumberYesTotal amount received from customer
changenumberYesChange returned to customer
deliveryobjectNonullReceipt delivery options (SMS/email)
delivery.customerPhonestringNonullPhone number for SMS delivery (format: +XXXXXXXXXXX)
delivery.customerEmailstringNonullEmail for receipt delivery
delivery.customerNamestringNonullCustomer name (max 60 chars)
delivery.languagestringNo"ro"Language for receipt: "ro", "en", "ru"
printReceiptbooleanNotruePrint receipt on POS printer

Response fields:

FieldTypeDescription
saleIdstringLocal sale UUID
purchaseIdstringServer-side purchase ID
serverSaleIdstringServer-side sale ID
fiscalOperationIdstringFiscal operation UUID
receiptNumberintSequential receipt number within current Z report
reportNumberintCurrent Z report number
fiscalCodestringFiscal code from MEV
mevIdstring | nullMEV transaction ID (null if offline)
isOfflinebooleantrue if MEV was unreachable (offline fiscal receipt)
offlineFiscalCodestring | nullOffline fiscal code (when operating offline)
totalnumberFinal sale total (after discounts)
subtotalnumberSubtotal before cart discount
amountReceivednumberAmount received from customer
changenumberChange returned
vatBreakdownobjectPer-VAT-code breakdown (see VAT Breakdown)
paymentBreakdownarrayArray of { type, amount } objects
qrCodeDatastringQR code URL for receipt verification
mevResponseXmlstringSOAP XML response from MEV
receiptDeliveryobject | null{ smsSent, emailSent } — indicates delivery was requested, not confirmed
createdAtstringISO 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:

ParameterTypeRequiredDefaultDescription
dateFromstringNoStart date (ISO: YYYY-MM-DD)
dateTostringNoEnd date (ISO: YYYY-MM-DD)
limitintNo50Max results per page
offsetintNo0Pagination offset

Response fields:

FieldTypeDescription
salesarrayArray of sale summaries
sales[].idstringSale UUID
sales[].purchaseIdstringServer purchase ID
sales[].serverSaleIdstringServer sale ID
sales[].fiscalOperationIdstringFiscal operation UUID
sales[].cashierIdstringCashier ID
sales[].cashierNamestringCashier name
sales[].subtotalnumberSubtotal amount
sales[].totalnumberTotal amount
sales[].paymentTypestringPrimary payment type label
sales[].paymentAmountnumberAmount paid
sales[].changeAmountnumberChange returned
sales[].statusstringSync status
sales[].createdAtstringISO 8601 timestamp
totalintTotal number of matching sales
limitintApplied limit
offsetintApplied 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:

ParameterTypeDescription
saleIdstringSale UUID

Response includes:

SectionDescription
items[]Product details: productId, productName, quantity, unitPrice, originalPrice, discountAmount, markupAmount, netAmount, vatCode, vatPercent, vatAmount
payments[]Payment details: paymentType, amount, notes
fiscalOperationFiscal 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:

FieldTypeRequiredDefaultDescription
amountnumberYesAmount to deposit (must be > 0)
reasonstringNonullReason for the deposit
printReceiptbooleanNotruePrint service receipt

Response fields:

FieldTypeDescription
operationIdstringOperation UUID
fiscalOperationIdstringFiscal operation UUID
receiptNumberintService receipt number
reportNumberintCurrent Z report number
mevIdstring | nullMEV transaction ID
isOfflinebooleanWhether operation was offline
amountnumberDeposited amount
newBalancenumberNew cash balance
operationTypestring"cashIn"
mevResponseXmlstringSOAP response XML
createdAtstringISO 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:

FieldTypeRequiredDefaultDescription
amountnumberYesAmount to withdraw (must be > 0)
reasonstringNonullReason for the withdrawal
printReceiptbooleanNotruePrint 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.

warning

This is an irreversible operation. It resets receipt counters and increments the report number.

Request body:

FieldTypeRequiredDefaultDescription
printReportbooleanNotruePrint the report

Requires: Terminal must be fiscalized and have valid MEV credentials.

Response fields:

FieldTypeDescription
reportNumberintZ report number
operationIdstringFiscal operation UUID
mevIdstring | nullMEV transaction ID
isOfflinebooleanWhether operation was offline
reportDataobjectFull report data (see Report Data below)
mevResponseXmlstringSOAP XML response from MEV
generatedAtstringISO 8601 timestamp

Report Data object:

FieldTypeDescription
reportNumberintZ report number
dailyTotalnumberTotal sales for the day
dailyTaxnumberTotal VAT for the day
dailyUntaxednumberUntaxed amount
vatBreakdownobjectPer-VAT-code breakdown
receiptsIssuedintNumber of receipts issued
lastReceiptNumberintLast receipt number
receiptsSentToMevintReceipts successfully sent to MEV
paymentTotalsobjectAmount per ACPS payment type code (e.g. { "1": 3000.00, "2": 2000.00 })
paymentCountsobjectCount per ACPS payment type code (e.g. { "1": 25, "2": 17 })
cashInnumberTotal cash deposits
cashOutnumberTotal cash withdrawals
cashBalancenumberCurrent cash balance
yearTotalnumber | nullCumulative yearly total (Z only)
yearTaxnumber | nullCumulative yearly tax (Z only)
totalSalesnumberTotal gross sales
totalReturnsnumberTotal returns amount
returnsCountintNumber of return operations
cashInCountintNumber of cash-in operations
cashOutCountintNumber of cash-out operations
generatedAtstringISO 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:

FieldTypeRequiredDefaultDescription
dateFromstringConditionalStart date (ISO: YYYY-MM-DD). Required for date range mode
dateTostringConditionalEnd date (ISO: YYYY-MM-DD). Required for date range mode
reportFromintConditionalStarting Z report number. For report range mode
reportTointConditionalEnding Z report number. For report range mode
detailedbooleanNofalseInclude individual Z report breakdown
printReportbooleanNotruePrint the report on POS printer

Filter modes (mutually exclusive — provide one set):

  1. Date range: dateFrom + dateTo
  2. Report number range: reportFrom and/or reportTo (both optional — omit reportFrom to start from Z#1, omit reportTo for latest)

Response fields:

FieldTypeDescription
filterModestring"dateRange" or "reportRange"
dateFromstringStart date (YYYY-MM-DD)
dateTostringEnd date (YYYY-MM-DD)
reportFromint(report range only) Starting Z report number
reportToint(report range only) Ending Z report number
totalSalesnumberAggregated total sales
totalTaxnumberAggregated total VAT
totalUntaxednumberAggregated untaxed amount
vatBreakdownobjectPer-VAT-code aggregated breakdown
receiptsCountintTotal receipts across all Z reports
paymentTotalsobjectAggregated payment totals by type
cashInnumberTotal cash deposits
cashOutnumberTotal cash withdrawals
cashInCountintNumber of cash-in operations
cashOutCountintNumber of cash-out operations
zReportsCountintNumber of Z reports in range
xReportsCountintNumber of X reports in range
firstZReportNumberintFirst Z report number in range
lastZReportNumberintLast Z report number in range
firstZReportTimestampstringFirst Z report close timestamp
lastZReportTimestampstringLast Z report close timestamp
totalReturnsnumberAggregated returns
returnsCountintNumber of return operations
individualZReportsarray | nullPer-Z breakdown (when detailed: true)
generatedAtstringISO 8601 timestamp

Individual Z Report fields (when detailed: true):

FieldTypeDescription
reportNumberintZ report number
closedAtstringISO 8601 timestamp when Z was closed
firstReceiptNumberintFirst receipt number in this Z
lastReceiptNumberintLast receipt number in this Z
receiptsCountintNumber of receipts
totalSalesnumberTotal sales for this Z
totalTaxnumberTotal VAT for this Z
vatBreakdownobjectVAT breakdown for this Z
paymentTotalsobjectPayment totals by type
cashInnumberCash deposits in this Z
cashOutnumberCash 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:

CodeCondition
FILTER_REQUIREDNeither date range nor report range provided
DATE_RANGE_INVALIDdateFrom is after dateTo
DATE_TO_FUTUREdateTo is in the future
REPORT_NUMBER_INVALIDreportFrom or reportTo < 1
REPORT_RANGE_INVALIDreportFrom > reportTo

5. Fiscal Queries

GET /api/fiscal/operations

Query fiscal operations from the audit database with filtering.

Query parameters:

ParameterTypeRequiredDescription
dateFromstringNoStart date (ISO: YYYY-MM-DD)
dateTostringNoEnd date (ISO: YYYY-MM-DD)
typestringNoOperation type filter (see Reference Tables)
statusstringNoOperation status filter (see Reference Tables)
limitintNoMax results (default 50)
offsetintNoPagination offset (default 0)

Response fields:

FieldTypeDescription
operationsarrayArray of fiscal operations
operations[].idstringOperation UUID
operations[].operationTypestringType of fiscal operation
operations[].receiptNumberintReceipt number
operations[].reportNumberintZ report number
operations[].mevIdstring | nullMEV transaction ID
operations[].statusstringOperation status
operations[].isOfflinebooleanWhether operated offline
operations[].totalAmountnumberOperation amount
operations[].vatBreakdownobjectSimplified VAT breakdown
operations[].vatDetailsobjectDetailed VAT with percent, rate, cost, gross
operations[].paymentsBreakdownobjectPayments by type
operations[].mevResponseXmlstringSOAP response XML
operations[].errorMessagestring | nullError message if operation failed
operations[].createdAtstringISO 8601 creation timestamp
operations[].completedAtstringISO 8601 completion timestamp
totalintTotal matching operations
limitintApplied limit
offsetintApplied 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:

FieldTypeRequiredDefaultDescription
linesarrayYesArray of line arrays
feedLinesintNo3Number of blank lines after content
cutbooleanNotrueCut paper after printing

Line format — each line is an array of 1-3 strings:

ColumnsLayout
["text"]Full width, left-aligned
["left", "right"]Left + right aligned
["left", "center", "right"]Three-column layout
[""]Empty line (spacer)
info

This is a non-fiscal operation — no MEV interaction, no fiscal receipt.

Response fields:

FieldTypeDescription
linesCountintNumber of lines printed
printedAtstringISO 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

CodeHTTP StatusDescription
MISSING_API_KEY401X-API-Key header not provided
INVALID_API_KEY401Provided API key is not valid
VALIDATION_ERROR400Request body validation failed (see details[])
INVALID_JSON400Request body is not valid JSON
TERMINAL_NOT_FISCAL409Terminal is not fiscalized (no MEV certificate)
MEV_CREDENTIALS_MISSING409Terminal is fiscalized but MEV credentials are missing
FISCAL_DAY_EXPIRED409>24h since last Z Report — Z Report required before continuing
INSUFFICIENT_CASH_BALANCE409Cash balance insufficient for the requested withdrawal
MEV_ERROR502Communication with MEV (SFS) failed (timeout, SOAP error, expired certificate)
BUSY429Another fiscal operation is in progress (30s timeout exceeded)
SALE_NOT_FOUND404Sale with specified ID not found
PRINTER_UNAVAILABLE503Printer is not available or disconnected

VAT Codes

CodeDescription
AVAT rate A (as configured on terminal)
BVAT rate B (typically 20%)
CVAT rate C
DVAT rate D
EVAT rate E
_Exempt (no VAT)

Payment Types (ACPS WSDL)

TypeDescription
"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

TypeDescription
receiptFiscal receipt (sale)
cash_inCash deposit
cash_outCash withdrawal
refundRefund/return
x_reportX Report
z_reportZ Report

Fiscal Operation Statuses

StatusDescription
pendingCreated, not yet sent to MEV
sentSent to MEV, awaiting response
successSuccessfully processed by MEV
failedMEV returned an error
offlineStored locally (MEV unreachable)

VAT Breakdown Object

{
"B": {
"code": "B",
"percent": 20.0,
"vatAmount": 7.50,
"taxableBase": 37.50,
"gross": 45.00
}
}
FieldTypeDescription
codestringVAT rate code
percentnumberVAT percentage
vatAmountnumberVAT amount
taxableBasenumberNet amount (excluding VAT)
grossnumberGross amount (including VAT)

Notes

  • All monetary amounts are in MDL (Moldovan Lei) as number with 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: true and mevId: null.
  • Receipt printing is fire-and-forget — the API response does not wait for printing to complete.